어썸오
스프링 애플리케이션이 로드되면, 우선 설정정보를 읽어온 후 클래스패스를 스캔하면서 BeanDefinition
을 생성하고 등록합니다. @ComponentScan
도 이때 동작합니다.
여담(중요하지 않음).
@Component
로 등록한 빈과@Bean
으로 등록한 빈은 각각 다른 스캐너에 의해 발견된다. 전자는ClassPathBeanDefinitionScanner
라는 빈이 클래스패스를 스캔하면서 애너테이션을 기반으로 데이터를 읽어오고 후자는ConfigurationClassBeanDefinitionReader
가@Configuration
이 붙은 클래스의 메서드를 살펴보면서 읽어옴
BeanFactory
는 이렇게 읽어온 BeanDefinition
정보를 바탕으로 인스턴스를 생성한 후 레지스트리에 등록합니다.
만약 의존관계 주입이 필요하다면 의존 대상 빈이 생성된 후 의존 관계 주입 과정을 거칩니다. 초기화 과정이 필요하면 초기화 콜백을 통해 초기화 과정을 거칩니다.
DefaultListableBeanFactory : 기본 구현체
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로 돌려보면 많은걸 얻을 수 있다.
많은 정보가 있지만 참고할만한 것 몇 가지를 집어보면
- 리소스를 뒤지면서 설정 정보를 읽어오고
- 메타데이터를 읽어오는데 필요한 빈들을 먼저 생성한다.
---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'
- 그리고 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'
- 만약 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()
- 의존성을 주입받아야하는 빈이라면 우선 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'
- 초기화 콜백을 통해 추가적인 초기화 과정을 진행한다.