본문 바로가기

프레임워크/Spring boot Core

1. IOC, DI Spring Boot탐구(1)

최근 Spring Boot에 대해서, 좀더 깊게 알아보고자 Spring boot 내부 코드를 Clone Coding하면서 알아가는 과정을 거쳤다. 그 내용을 정리해보고자 한다.

 

https://github.com/diqksrk/spring-boot-ioc-di

 

GitHub - diqksrk/spring-boot-ioc-di

Contribute to diqksrk/spring-boot-ioc-di development by creating an account on GitHub.

github.com

Annotation Application Context

기존의 XML방식에서 벗어나 @Annotation을 활용하여 새로운 장을 열게 해준 클래스이다.

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    private final AnnotatedBeanDefinitionReader reader;

    /**
     * Register one or more component classes to be processed.
     * <p>Note that {@link #refresh()} must be called in order for the context
     * to fully process the new classes.
     * @param componentClasses one or more component classes &mdash; for example,
     * {@link Configuration @Configuration} classes
     * @see #scan(String...)
     * @see #refresh()
     */
    @Override
    public void register(Class<?>... componentClasses) {
        Assert.notEmpty(componentClasses, "At least one component class must be specified");
        StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
                .tag("classes", () -> Arrays.toString(componentClasses));
        this.reader.register(componentClasses);
        registerComponentClass.end();
    }
}

Register Method를 사용해서 Bean을 등록한다.

AnnotatedBeanDefinitionReader

public class AnnotatedBeanDefinitionReader {
    public void register(Class<?>... componentClasses) {
        for (Class<?> componentClass : componentClasses) {
            registerBean(componentClass);
        }
    }

    public void registerBean(Class<?> beanClass) {
        doRegisterBean(beanClass, null, null, null, null);
    }

    private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                                    @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                                    @Nullable BeanDefinitionCustomizer[] customizers) {

        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }

        abd.setInstanceSupplier(supplier);
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }
        if (customizers != null) {
            for (BeanDefinitionCustomizer customizer : customizers) {
                customizer.customize(abd);
            }
        }

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
}

1. registerBean 메소드에서는 componet annotation이 붙은 class들을 인자로 받는다. 그리고 doRegisterBean을 호출한다.

2. doRegisterBean에서는 빈정의와 이름을 가지는 BeanDefinitionHolder를 생성한다.

3. 이후 BeanDefinitionReaderUtils의 registerBeanDefinition을 호출하고 위애서 생성한 BeanDefinitionHolder와 인자로 내부 클래스 변수인 BeanDefinitionRegistry를 인자로 넘긴다.

 

BeanDefinitionReaderUtils의 registerBeanDefinition이 중요하다 !

public class BeanDefinitionReaderUtils {
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }
}

1. 전달받은 BeanDefinitionHolder로부터 beanName을 전달받는다.

2. 전달받은 BeanName을 통해 registry에 의해 등록된다.

 

그렇다면 BeanDefinitionRegistry는 뭐하는 애일까?

public class AnnotatedBeanDefinitionReader {
    private final BeanDefinitionRegistry registry;

    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
        this(registry, getOrCreateEnvironment(registry));
    }

1. 생성자를 살펴보면, 생성될때 인자로 받은 변수에 의해 Registry를 통해 생성된다.

public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
   super(beanFactory);
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

자기 자신을 넘긴다.

public class GenericApplicationContext extends AbstractApplicationContext {
    private final AbstractBeanFactory beanFactory;

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
    }