ApplicationContext Refresh - refreshContext() 전반부

appti·2024년 3월 14일
0

분석

목록 보기
14/23

환경

버전은 스프링 부트 3.0.8이며, Spring MVC를 사용했습니다.

@SpringBootApplication이 명시된 클래스는 위와 같습니다.

서론

이전 글에서 createApplicationContext(), context.setApplicationStartup(), prepareContext()까지 살펴봤으며 지금 글에서는 refreshContext(), 그 중 AbstractApplicationContext.refresh() 메서드의 try-catch 블록 밖에 존재하는 전반부를 확인해보고자 합니다.

SpringApplication.refreshContext()

ApplicationContext를 refresh, 초기화하는 메서드입니다.

SpringApplicationShutdownHook.registerAplicationContext()를 통해 shutdownHook을 등록합니다.
이를 통해 스프링 부트 애플리케이션 종료 시 graceful shutdown이 가능해집니다.

shutdownHook 등록 이후 refresh를 수행합니다.

super.refresh()

AnnotationConfigServletWebServerApplicationContext.refresh()를 호출하면 다형성으로 인해 AbstractApplicationContext.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);
        
        ...
}

해당 영역의 코드만을 확인합니다.

this.applicationStartup.start("spring.context.refresh")

모니터링 용도의 ApplicationStartup을 가져옵니다.

DefeaultApplicationStartup의 상수를 가져옵니다.

prepareRefresh()

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를 관리할 수 있는 필드를 초기화합니다.

obtainFreshBeanFactory()

단 하나만 초기화되었다고 확신할 수 있는 BeanFactory를 조회합니다.

현재 ApplicationContext는 AnnotationConfigServletWebServerApplicationContext이므로, 오버라이딩된 GenericApplicationContext.refreshBeanFactory()를 호출합니다.

CAS 연산으로 BeanFactory가 단 한 번만 refresh 되었는지 검증합니다.
이는 하나의 ApplicationContext에서는 하나의 BeanFactory만 사용하도록 설정한 것이라고 볼 수 있습니다.

prepareBeanFactory()

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 과정 중 필요한 최소한의 빈이 등록됩니다.

profile
안녕하세요

0개의 댓글