버전은 스프링 부트 3.0.8이며, Spring MVC를 사용했습니다.
@SpringBootApplication이 명시된 클래스는 위와 같습니다.
이전 글에서 createApplicationContext(), context.setApplicationStartup(), prepareContext()까지 살펴봤으며 지금 글에서는 refreshContext(), 그 중 AbstractApplicationContext.refresh() 메서드의 try-catch 블록 밖에 존재하는 전반부를 확인해보고자 합니다.
ApplicationContext를 refresh, 초기화하는 메서드입니다.
SpringApplicationShutdownHook.registerAplicationContext()를 통해 shutdownHook을 등록합니다.
이를 통해 스프링 부트 애플리케이션 종료 시 graceful shutdown이 가능해집니다.
shutdownHook 등록 이후 refresh를 수행합니다.
AnnotationConfigServletWebServerApplicationContext.refresh()를 호출하면 다형성으로 인해 AbstractApplicationContext.refresh()가 호출됩니다.
@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();
}
}
}
위 코드가 AbstractApplicationContext.refresh() 메서드의 전체 코드입니다.
@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);
...
}
해당 영역의 코드만을 확인합니다.
모니터링 용도의 ApplicationStartup을 가져옵니다.
DefeaultApplicationStartup의 상수를 가져옵니다.
refresh를 준비합니다.
AnnotationConfigServletWebServerApplicationContext.prepareRefresh()가 호출된 이후 super.prepareRefresh()로 인해 AbstractApplicationContext.prepareRefresh()가 호출됩니다.
ApplicationContext가 종료되었음을 나타내는 closed를 false, ApplicationContext가 동작중임을 나타내는 active를 true로 설정합니다.
이 값들로 인해 빈 조회 가능 여부가 결정되므로, 반드시 ApplicationContext refresh 시에 가장 먼저 호출해야 합니다.
initPropertySources()를 통해 환경 설정을 수행합니다.
현재 ApplicationContext는 AnnotationConfigServletWebServerApplicationContext이므로, 오버라이딩된 GenericWebApplicationContext.initPropertySources()를 호출합니다.
내부적으로 WebApplicationContextUtils.initServletPropertySources를 호출합니다.
Environment에 지정된 propertySources 중 StubPropertySource를 파라미터로 넘어오는 ServletContext 혹은 ServletConfig에 맞는, 실제 인스턴스로 교체하는 역할을 수행합니다.
다만 이 시점에서는 ServletContext와 ServletConfig 모두 값이 없기 때문에 동작하지 않습니다.
validateRequiredProperties()를 호출합니다.
해당 메서드는 AbstractPropertyResolver.validateRequiredProperties()를 호출합니다.
다만 별도로 setRequiredProperties()를 통해 ApplicationContext refresh 시점에서 반드시 존재해야 하는 properties를 지정하지 않으면 동작하지 않습니다.
이후 이전에 등록된 ApplicationListener를 earlyApplicationListeners에 Set으로 저장합니다.
지금 시점은 막 ApplicationContext를 refresh 하는 시점이기 때문에, 반드시 earlyApplicationListeners가 null입니다.
위의 분기문으로 인해 earlyApplicationListeners가 반드시 null이 아님을 확인할 수 있기 때문에 이후 ApplicationContext refresh 이전에 발생한 ApplicationEvent를 관리할 수 있는 필드를 초기화합니다.
단 하나만 초기화되었다고 확신할 수 있는 BeanFactory를 조회합니다.
현재 ApplicationContext는 AnnotationConfigServletWebServerApplicationContext이므로, 오버라이딩된 GenericApplicationContext.refreshBeanFactory()를 호출합니다.
CAS 연산으로 BeanFactory가 단 한 번만 refresh 되었는지 검증합니다.
이는 하나의 ApplicationContext에서는 하나의 BeanFactory만 사용하도록 설정한 것이라고 볼 수 있습니다.
BeanFactory를 사용할 수 있도록 사전 작업을 수행합니다.
순서대로 클래스 로더, spEL 표현식 Resolver, 빈 Properties의 타입 변환을 수행하는 Registrar를 등록합니다.
추후 등록된 ApplicationContextAware 타입의 빈이 ApplicationContext를 활용해 필요한 정보에 접근할 수 있도록 ApplicationContextAwareProcessor를 등록합니다.
이후, ApplicationContextAware 타입의 빈이 ApplicationContextAwareProcessor를 통해서만 ApplicationContext에 접근할 수 있어야 하므로 다른 경우에는 ApplicationContext가 주입되지 않도록 ignoreDependencyInterface()로 지정합니다.
이후 BeanFactory가 특정 타입의 빈들의 의존 관계를 어떤 식으로 풀어낼 수 있을지 명시합니다.
예를 들어, beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory)의 경우 다른 빈이 BeanFactory에 대한 의존성을 갖게 된다면 자기 자신을 주입하라는 의미입니다.
ApplicationListenerDetector를 등록합니다.
해당 BeanPostProcessor는 이름 그대로 ApplicationListener를 구현한 빈을 감지하고 등록하는 역할을 수행합니다.
@EventListener로 등록한 ApplicationListener까지 감지가 가능합니다.
네이티브 이미지가 아니며, LoadTimeWeaver 빈이 등록된 경우 관련된 빈에 LoadTimeWeaver를 주입하는 BeanPostProcessor를 등록합니다.
현재 환경에서는 동작하지 않습니다.
기본적인, 환경 설정과 관련된 빈들을 등록합니다.
ApplicationContext를 refresh 할 수 있도록 ApplicationStartup 조회, ApplicationContext 상태 값(closed, active) 설정 및 BeanFactory 사전 작업을 수행합니다.
이 단계에서 ApplicationContext refresh 과정 중 필요한 최소한의 빈이 등록됩니다.