해당 포스팅은 [Spring Boot] 대체 어떻게 실행되는걸까 ? 포스팅의 일부입니다.
2022.05.01 - [Java] - [Spring Boot] 대체 어떻게 실행되는걸까 ?
Spring Boot의 ApplicationContext가 초기화되는 과정에서 refresh 과정이 있습니다. 이번 포스팅을 통해 refresh가 어떻게 되는지 알아보겠습니다.
this.refreshContext(context)는 다음 코드로 이어집니다.
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
this.refresh(context);
}
Spring은 프로그램이 종료될 때 ApplicationContext가 gracefully shutdown 될 수 있도록 관련된 함수를 호출합니다. Spring Boot는 다음과 같이 @Predestroy 어노테이션을 통해 shutdownHook을 등록할 수 있습니다.
그럼 본격적으로 this.refresh(context); 가 어떻게 동작하는지 알아보겠습니다. refresh 메서드를 따라 들어가면 AbstractApplicationContext의 refresh메서드가 등장합니다. 각 코드를 차근차근 분석해보겠습니다.
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 1
this.prepareRefresh(); // 2
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); // 3
this.prepareBeanFactory(beanFactory); // 4
try {
this.postProcessBeanFactory(beanFactory); // 5
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // 6
this.invokeBeanFactoryPostProcessors(beanFactory); // 7
this.registerBeanPostProcessors(beanFactory); // 8
beanPostProcess.end(); // 9
this.initMessageSource(); // 10
this.initApplicationEventMulticaster(); // 11
this.onRefresh(); // 12
this.registerListeners(); // 13
this.finishBeanFactoryInitialization(beanFactory); // 14
this.finishRefresh(); // 15
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches(); // 17
contextRefresh.end(); // 18
}
}
}
일단 AbstractApplicationContext의 refresh 메서드의 용도부터 확인하겠습니다. refresh 메서드는 Spring boot의 configuration과 관련이 있습니다. Java-based, XML file, properties file 등의 소스로부터 spring boot의 configuration을 등록하거나 최신화(refresh)합니다.
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
ApplicationStartup 클래스의 start 메서드를 통해서 "spring.context.refresh" step이 실시됨을 표시합니다. (19) 번의 contextRefresh.end() 메서드를 통해서 해당 step이 종료됨을 알립니다.
this.prepareRefresh();
ApplicationContext refresh를 위한 준비하기 위한 작업을 진행합니다. startupDate, active flag을 설정하고 property source를 초기화하는 작업을 진행합니다.
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
BeanFactory를 가져오는 코드입니다.
this.prepareBeanFactory(beanFactory)
이전 코드에서 가져온 BeanFactor에 ClassLoader 또는 post-processor를 등록합니다.
this.postProcessBeanFactory(beanFactory);
BeanFactory의 초기화 단계 이후 추가적인 설정을 하는 코드입니다. Bean definition은 등록된 상태이지만 아직 bean이 초기화되지 않은 단계에 호출됩니다. 따라서 해당 단계에 필요한 로직을 추가할 수 있습니다.
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
(1) 번과 동일하게 startup step을 생성하는 코드입니다. (9) 번 코드를 통해서 해당 startup step을 종료하는 것을 확인할 수 있습니다.
this.invokeBeanFactoryPostProcessors(beanFactory)
등록된 모든 BeanFactoryPostProcessor bean을 초기화 후 실행합니다.
this.registerBeanPostProcessors(beanFactory);
BeanPostProcessor을 모두 등록합니다. 등록된 BeanPostProcessor은 (14) 번의 this.finishBeanFactoryInitialization(beanFactory)에 의해 호출됩니다.
beanPostProcess.end();
beanPostProcess step을 종료 처리합니다.
this.initMessageSource();
MessageSource를 초기화합니다.
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if (beanFactory.containsLocalBean("messageSource")) {
this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class);
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource;
if (hms.getParentMessageSource() == null) {
hms.setParentMessageSource(this.getInternalParentMessageSource());
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using MessageSource [" + this.messageSource + "]");
}
} else {
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(this.getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton("messageSource", this.messageSource);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No 'messageSource' bean, using [" + this.messageSource + "]");
}
}
}
this.initApplicationEventMulticaster
ApplicationListener에게 event를 publish할 때 사용하는 ApplicationEventMulticaster을 초기화합니다.
this.onRefresh()
Template 패턴이 적용된 방식입니다. onRefresh()메서드 Overriding을 통해서 context-specific 로직을 추가할 수 있습니다.
this.registerListeners();
ApplicationListener을 구현한 bean을 등록합니다.
this.finishBeanFactoryInitialization(beanFactory)
ApplicationContext의 beanFactory 초기화 단계를 마무리합니다. 남은 singleton bean을 모두 초기화합니다. 또한 BeanPostProcessor으로 등록된 bean의 postProcessBeforeInitialization, postProcessAfterInitialization 메서드를 호출하는 단계입니다.
this.finishRefresh()
refresh 과정이 끝남을 알립니다. LifecycleProcessor의 onRefresh() 메서드를 호출하고 ContextRefreshedEvent를 publish 합니다.
protected void finishRefresh() {
this.clearResourceCaches();
this.initLifecycleProcessor();
this.getLifecycleProcessor().onRefresh();
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
if (!NativeDetector.inNativeImage()) {
LiveBeansView.registerApplicationContext(this);
}
}
this.resetCommonCaches()
Spring의 refrection metadata cache를 제거합니다.
protected void resetCommonCaches() {
ReflectionUtils.clearCache();
AnnotationUtils.clearCache();
ResolvableType.clearCache();
CachedIntrospectionResults.clearClassLoader(this.getClassLoader());
}
contextRefresh.end();
(1) 번에서 생성한 StartupStep객체의 종료를 표시합니다.
'Java > Spring Boot' 카테고리의 다른 글
[Spring Boot]Spring Boot JPA는 MySQL에서 어떤 쿼리를 실행시킬까? (0) | 2022.10.29 |
---|---|
[Spring Boot] @Transactional이 COMMIT을 실행하는 코드까지 (0) | 2022.08.06 |
[Spring Boot] Bean이 살고있는 집 ApplicationContext 기능 살펴보기 (0) | 2022.05.05 |
[Spring Boot] 알쏭달쏭한 환경변수 설정원리 (0) | 2022.05.01 |
[Spring Boot] 대체 어떻게 실행되는걸까 ? (0) | 2022.05.01 |