Skip to content

Latest commit

 

History

History
176 lines (115 loc) · 8.66 KB

54.md

File metadata and controls

176 lines (115 loc) · 8.66 KB

스프링 Bean의 생성 과정을 설명해주세요.

어썸오

스프링 애플리케이션이 로드되면, 우선 설정정보를 읽어온 후 클래스패스를 스캔하면서 BeanDefinition을 생성하고 등록합니다. @ComponentScan도 이때 동작합니다.

여담(중요하지 않음). @Component로 등록한 빈과 @Bean으로 등록한 빈은 각각 다른 스캐너에 의해 발견된다. 전자는 ClassPathBeanDefinitionScanner라는 빈이 클래스패스를 스캔하면서 애너테이션을 기반으로 데이터를 읽어오고 후자는 ConfigurationClassBeanDefinitionReader@Configuration이 붙은 클래스의 메서드를 살펴보면서 읽어옴

BeanFactory는 이렇게 읽어온 BeanDefinition 정보를 바탕으로 인스턴스를 생성한 후 레지스트리에 등록합니다.

만약 의존관계 주입이 필요하다면 의존 대상 빈이 생성된 후 의존 관계 주입 과정을 거칩니다. 초기화 과정이 필요하면 초기화 콜백을 통해 초기화 과정을 거칩니다.

학습 자료

BeanFactory

DefaultListableBeanFactory : 기본 구현체

Bean


추가 학습

BeanDefinition은 다음과 같은 정보들이 저장되어 있는 객체임

  • package-qualified class name
  • 빈이 컨테이너 안에서 어떻게 행동해야하는지에 대한 설정(스코프, 생명주기 콜백 등)
  • 의존관계 정보

빈을 생성하고 등록하는 메서드가 궁금하면 ConfigurableApplicationContext.refresh() 메서드를 참고하면 된다.

  • 자바 코드 설정, xml 파일 설정들을 갱신 혹은 불러오는 메서드. 이 메서드를 통해 BeanFactory의 초기화가 이루어지고 빈이 등록된다.

실제 구현부는 AbstractApplicationContext에 정의되어 있다.

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
                contextRefresh.end();
            }
        }
    }
    
}

더 궁금한 사람들을 위해

스프링부트 애플리케이션을 로그 레벨 trace로 돌려보면 많은걸 얻을 수 있다.

많은 정보가 있지만 참고할만한 것 몇 가지를 집어보면

  1. 리소스를 뒤지면서 설정 정보를 읽어오고
  2. 메타데이터를 읽어오는데 필요한 빈들을 먼저 생성한다.
---2022-09-18 23:43:55.165 -TRACE 53224 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.createBean : Creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'

---2022-09-18 23:43:56.544 -TRACE 53224 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.createBean : Creating instance of bean 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory'


  1. 그리고 bean definition을 먼저 생성하여 등록한다.
---2022-09-18 23:43:56.659 -TRACE 53224 --- [           main] org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod : Registering bean definition for @Bean method com.example.tmp1.Tmp1Application.mySingletonBean()


4. Bean Definition을 바탕으로 빈을 생성한다
---2022-09-19 00:06:12.080 -TRACE 55491 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.createBean : Creating instance of bean 'mySingletonBean'


  1. 만약 aop를 사용하는 빈이라면 프록시 객체를 등록하는 과정이 이후에 추가된다(BeanPostProcessor)
---2022-09-19 00:19:26.019 -TRACE 57094 --- [           main] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource.getTransactionAttribute : Adding transactional method 'com.example.tmp1.MyService.someTransactionalMethod' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
---2022-09-19 00:19:26.021 -TRACE 57094 --- [           main] org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.buildAdvisors : Creating implicit proxy for bean 'myService' with 0 common interceptors and 1 specific interceptors
---2022-09-19 00:19:26.022 -TRACE 57094 --- [           main] org.springframework.aop.framework.CglibAopProxy.getProxy : Creating CGLIB proxy: SingletonTargetSource for target object [com.example.tmp1.MyService@615439f7]
---2022-09-19 00:19:26.024 -TRACE 57094 --- [           main] org.springframework.aop.framework.CglibAopProxy.accept : Unable to apply any optimizations to advised method: public void com.example.tmp1.MyService.someTransactionalMethod()


  1. 의존성을 주입받아야하는 빈이라면 우선 CGLIB을 이용해 객체를 생성한 후, 의존 관계인 빈이 생성되면 주입받는다.
---2022-09-19 00:48:15.049 -DEBUG 60025 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.createArgumentArray : Autowiring by type from bean name 'myController' via constructor to bean named 'myService'
---2022-09-19 00:48:15.050 -TRACE 60025 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBean : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
---2022-09-19 00:48:15.050 -TRACE 60025 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBean : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
---2022-09-19 00:48:15.050 -TRACE 60025 --- [           main] org.springframework.beans.factory.support.DefaultListableBeanFactory.createBean : Finished creating instance of bean 'myController'

  1. 초기화 콜백을 통해 추가적인 초기화 과정을 진행한다.