[스프링 빈 생성 과정 분석 시리즈]
- 스프링 빈 생성 과정 분석 [1] - Application Context(BeanFactory)
- 스프링 빈 생성 과정 분석 [2] - BeanDefinitionRegistry, SingletonBeanRegistry
- 스프링 빈 생성 과정 분석 [3] - BeanFactoryPostProcessor, BeanPostProcessor
- 스프링 빈 생성 과정 분석 [4] - 디버깅 참고 자료
1편부터 3편까지 하여 스프링 빈 생성 과정이 어떻게 이뤄지는지 알아보았다.
이번 포스팅에서는 이 개념들을 찾아보기 위해 실제 디버깅한 소스 코드를 공유하기 위하 작성하였다.
스프링 부트의 run 메서드를 통해 시간순으로 어떻게 흘러가는지 코드만 공유하겠다.
이를 통해 디버깅한다면 1-3편에서 설명한 빈 생성 과정의 실제 과정을 확인할 수 있을 것이다.
[SpringApplication 클래스 - run 메서드]
public ConfigurableApplicationContext run(String... args) {
... 생략
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = this.printBanner(environment);
// 1. applicationContext 생성
context = this.createApplicationContext();
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 2. beanDefinition 등록 및 빈 인스턴스화
this.refreshContext(context);
... 생략
}
1. applicationContext 생성 - [DefaultApplicationContextFactory.class]
run 함수의 context = this.createApplicationContext();를 추적하면 DefaultApplicationContextFactory.class의 create 메서드의 반환값이 applicationContext로 사용됨을 알 수 있다.
이 create 함수는 다시 한번 getFromSpringFactories 메서드를 호출하여 해당 메서드의 리턴값을 넘기는데
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
try {
// getFromSpringFactories의 결과를 리턴
return (ConfigurableApplicationContext)this.getFromSpringFactories(webApplicationType, ApplicationContextFactory::create, this::createDefaultApplicationContext);
} catch (Exception var3) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var3);
}
}
getFromSpringFactories는 세번째 매개변수에 this::createDefaultApplicationContext를 넘기고 있다.
this::createDefaultApplicationContext는 이 클래스의 createDefaultApplicationContext 메서드이다.
private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
Iterator var4 = SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, this.getClass().getClassLoader()).iterator();
Object result;
do {
if (!var4.hasNext()) {
// defaultResult가 존재하면 defaultResult.get()을 리턴
return defaultResult != null ? defaultResult.get() : null;
}
ApplicationContextFactory candidate = (ApplicationContextFactory)var4.next();
result = action.apply(candidate, webApplicationType);
} while(result == null);
return result;
}
getFromSpringFactories는 세번째 파라미터인 defaultResult가 존재하면 defaultResult의 get() 함수를 호출하여 해당 함수의 결과를 리턴한다.
(* Supplier<T> 타입은 함수형 인터페이스로 이 타입을 매개변수로 선언하면 함수를 매개변수로 받을 수 있다.)
private ConfigurableApplicationContext createDefaultApplicationContext() {
return (ConfigurableApplicationContext)(!AotDetector.useGeneratedArtifacts() ? new AnnotationConfigApplicationContext() : new GenericApplicationContext());
}
createDefaultApplicationContext 함수를 보면 위와 같이 AotDetector의 특정 값을 통해 AnnotationConfigApplicationContext 또는 GenericApplicationContext를 반환하게 되는데 별도의 설정을 하지 않는다면 AnnotationConfigApplicationContext이 반환되게 된다.
2. beanDefinition 등록 및 빈 인스턴스화
스프링 부트의 autoConfig 방식에서 run 메서드가 applicationContext를 생성하는 방식은 알게 되었으니 실제로 빈이 인스턴스화 되는 곳을 알아보자.
run 메서드의 this.refreshContext(context);를 통해 빈이 인스턴스화 된다.
refreshContext 메서드를 추적하면 아래와 같이 ConfigurableApplicationContext타입의 refresh() 메서드를 호출하는 것을 알 수 있는데
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
우리는 이전에 AnnotationConfigApplicationContext가 기본 applicationContext로 사용되는 것을 알았으니
AnnotationConfigApplicationContext를 통해 refresh() 메서드를 추적하면 AbstractApplicationContext가 해당 메서드를 구현하고 있음을 알 수 있다.
public void refresh() throws BeansException, IllegalStateException {
this.startupShutdownLock.lock();
try {
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 빈 팩토리 설정(클래스 로더, beanPostProcessor, ResourceLoader, ApplicationEventPublisher,
// Environment, MessageSource 등 설정)
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// beanFactoryPostProcessor 등록(beanDefinition 등록)
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
// 빈 인스턴스화
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (Error | RuntimeException var12) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var12);
}
this.destroyBeans();
this.cancelRefresh(var12);
throw var12;
} finally {
contextRefresh.end();
}
} finally {
this.startupShutdownThread = null;
this.startupShutdownLock.unlock();
}
}
- this.prepareBeanFactory(beanFactory);
beanFactory와 관련된 설정을 하는 메서드로 클래스 로더, Environment, beanPostProcessor, ResourceLoader, ApplicationEventPublisher, MessageSource 등을 설정한다. - this.invokeBeanFactoryPostProcessors(beanFactory);
beanFactoryPostProcessor를 등록한다.
이 메서드를 추적하면 beanFactoryPostProcessor 구현체를 뽑아와 실행시키는 로직을 볼 수 있다.
private static void invokeBeanFactoryPostProcessors(Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
Iterator var2 = postProcessors.iterator();
while(var2.hasNext()) {
BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var2.next();
StartupStep var10000 = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process");
Objects.requireNonNull(postProcessor);
StartupStep postProcessBeanFactory = var10000.tag("postProcessor", postProcessor::toString);
// 빈 팩토리 후처리 실행(beanDefinition 등록)
postProcessor.postProcessBeanFactory(beanFactory);
postProcessBeanFactory.end();
}
}
대게 postProcessBeanFactory를 통해 beanDefinition을 정의한다. 대표적인 예로 ConfigurationClassPostProcessor가 있는데 이 클래스가 @Component, @Configuration을 통해 정의한 클래스의 BeanDefinition을 등록한다.
- this.finishBeanFactoryInitialization(beanFactory);
이 메서드의 마지막 라인에 beanFactory.preInstantiateSingletons(); 이라는 메서드가 있다.
해당 메서드는 DefaultListableBeanFactory에 존재하는데 이 메서드를 보면 getBean(beanName) 메서드를 확인 할 수 있을 것이다.
우리는 이전에 getBean(beanName)을 통해 빈이 없으면 beanDefintion을 토대로 빈을 생성하여 가져오는 로직을 확인하였다.
'Framework & Lib & API > 스프링' 카테고리의 다른 글
[스프링] 스태틱 메서드가 아닌 스프링 싱글톤 빈을 사용해야하는 이유 (0) | 2024.06.29 |
---|---|
스프링의 트랜잭션 추상화 (0) | 2024.06.25 |
AbstractRoutingDataSource에서 Transactional readonly값 false만 리턴하는 오류 해결 (0) | 2024.06.16 |
스프링 빈 생성 과정 분석 [3] - BeanFactoryPostProcessor, BeanPostProcessor (2) | 2024.04.20 |
스프링 빈 생성 과정 분석 [2] - BeanDefinitionRegistry, SingletonBeanRegistry (0) | 2024.04.20 |