IoC Conatiner in SpringApplication.run() - 3

koonlx·2024년 11월 9일

Spring

목록 보기
7/7

IoC Container Refresh

refresh 메소드

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		this.startupShutdownLock.lock();
		try {
			this.startupShutdownThread = Thread.currentThread();

			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 (RuntimeException | Error 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 {
				contextRefresh.end();
			}
		}
		finally {
			this.startupShutdownThread = null;
			this.startupShutdownLock.unlock();
		}
	}
  • startupShutdownLock 및 startupShutdownThread
    • 락과 스레드 설정: startupShutdownLock을 걸어 싱글 쓰레드 환경을 보장하고, 현재 스레드를 startupShutdownThread에 설정하여 컨텍스트가 올바르게 초기화되도록 한다.
  • applicationStartup 단계: spring.context.refresh
    • StartupStep 생성: this.applicationStartup.start("spring.context.refresh")는 refresh()의 성능 추적을 위해 시작 단계(StartupStep)를 생성한다.
  • prepareRefresh()
    • 리프레시 준비: prepareRefresh()는 컨텍스트 설정을 재구성하고, 시스템 속성을 준비하며, 이전 리소스를 정리한다.
  • obtainFreshBeanFactory()
    • 새 BeanFactory 획득: 컨텍스트에서 사용될 새로운 BeanFactory를 생성하고 반환한다. 이 과정에서 내부적으로 DefaultListableBeanFactory가 생성되거나 재설정된다.
  • prepareBeanFactory(beanFactory)
    • BeanFactory 준비: beanFactory를 초기화하고, ApplicationContext에 필요한 표준 Bean들을 추가하며, ClassLoader나 PropertyEditor 등을 설정한다.
  • postProcessBeanFactory(beanFactory)
    • BeanFactory 후처리: 하위 클래스가 필요한 추가 처리를 beanFactory에 적용할 수 있도록 오버라이딩 메서드를 제공하는 지점이다.
  • invokeBeanFactoryPostProcessors(beanFactory) 및 registerBeanPostProcessors(beanFactory)
    • BeanFactory 포스트 프로세서 호출: 모든 BeanFactoryPostProcessor를 실행해 빈 설정을 조정한다.
    • BeanPostProcessor 등록: BeanPostProcessor 인터페이스를 구현한 빈들을 등록하여 빈 초기화 과정에 개입할 수 있도록 설정한다.
  • initMessageSource()
    • 메시지 소스 초기화: 국제화(i18n) 메시지 처리를 위한 MessageSource 빈을 초기화한다.
  • initApplicationEventMulticaster()
    • 이벤트 멀티캐스터 초기화: 애플리케이션 이벤트 전파를 관리할 ApplicationEventMulticaster를 초기화한다.
  • onRefresh()
    • 하위 클래스의 추가 작업: 특정 컨텍스트 하위 클래스에서 필요한 추가 초기화 작업을 수행할 수 있는 단계이다.
  • registerListeners()
    • 이벤트 리스너 등록: ApplicationListener 인터페이스를 구현한 리스너를 등록하여 이벤트 시스템에 통합한다.
  • finishBeanFactoryInitialization(beanFactory)
    • 싱글톤 빈 초기화: 모든 싱글톤 빈(지연 초기화가 아닌 빈)을 인스턴스화하고 초기화하는 과정이다.
  • finishRefresh()
    • 리프레시 완료 이벤트 발행: ContextRefreshedEvent를 발행하여 애플리케이션이 준비 상태임을 알린다.
  • 예외 처리: RuntimeException 또는 Error
    • 예외 처리: 예외 발생 시 로그를 남기고, 생성된 싱글톤 빈을 제거하며, cancelRefresh(ex)로 초기화 상태를 취소하고 예외를 상위로 전달한다.
  • startupShutdownThread 초기화 및 startupShutdownLock 해제
    • 마무리 작업: startupShutdownThread를 null로 설정하고, 락을 해제하여 메서드를 종료한다.

finishBeanFactoryInitialization 메소드

	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no BeanFactoryPostProcessor
		// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			try {
				beanFactory.getBean(weaverAwareName, LoadTimeWeaverAware.class);
			}
			catch (BeanNotOfRequiredTypeException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to initialize LoadTimeWeaverAware bean '" + weaverAwareName +
							"' due to unexpected type mismatch: " + ex.getMessage());
				}
			}
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}

finishBeanFactoryInitialization 메서드는 Spring IoC 컨테이너의 refresh 과정에서 마지막 단계로, 모든 싱글톤 빈을 초기화하고 컨텍스트의 완전한 가동을 보장한다. 이 메서드는 애플리케이션의 안정적인 실행을 위해 몇 가지 중요한 작업을 순차적으로 수행한다.

첫 번째 작업은 컨버전 서비스(ConversionService)를 초기화하는 것이다. 컨텍스트에서 ConversionService라는 빈이 정의되어 있는 경우, 해당 빈을 beanFactory에 설정하여 타입 변환을 위한 중앙화된 서비스를 제공한다. 이는 빈 프로퍼티의 자동 바인딩 과정에서 필요한 다양한 타입 변환을 지원하기 위해 필수적이다.

두 번째 단계는 기본 임베디드 값 해석기를 등록하는 과정이다. beanFactory에 값 해석기가 등록되어 있지 않은 경우, 기본 해석기를 추가해 프로퍼티나 애노테이션에 포함된 플레이스홀더가 올바르게 해석되도록 한다. 이 해석기는 예를 들어 ${propertyName}과 같은 형태의 값을 환경 변수로 대체하는 기능을 수행한다.

다음으로 LoadTimeWeaverAware 인터페이스를 구현한 빈들을 초기화한다. 로드 타임 위버(Load Time Weaver)는 빈이 로딩되는 시점에 클래스 변환을 가능하게 하며, AOP(Aspect-Oriented Programming)와 같은 기능을 지원한다. 이 단계에서는 LoadTimeWeaverAware 빈이 미리 초기화되어 클래스 변환기를 조기에 등록할 수 있도록 한다. 만약 기대한 타입과 맞지 않는 빈이 발견되면, 디버그 로그를 통해 알림을 남긴다.

그 후, beanFactory에 임시로 사용했던 ClassLoader를 제거한다. 이는 타입 매칭을 위해 임시로 사용했던 ClassLoader를 해제하여 리소스를 확보하고, 이후의 실행에서 필요 없는 클래스 로딩을 방지한다.

빈 정의 메타데이터의 캐싱도 이 단계에서 이루어진다. beanFactory.freezeConfiguration() 메서드를 호출하여 모든 빈 정의 메타데이터를 동결함으로써, 이후 설정이 변경되지 않도록 하고, 성능을 최적화한다. 빈 정의를 동결하면 더 이상 빈 설정에 대한 변경을 허용하지 않아 애플리케이션이 더욱 안정적으로 실행될 수 있다.

마지막으로, preInstantiateSingletons() 메서드를 호출하여 남아 있는 모든 싱글톤 빈을 인스턴스화한다. 이 과정에서 빈들이 미리 초기화되고 의존성이 주입되어 애플리케이션이 즉시 실행될 준비가 갖추어진다. 이로써 finishBeanFactoryInitialization 메서드는 컨텍스트 초기화를 마무리 짓고, 애플리케이션이 정상적으로 가동될 수 있도록 보장한다.

마침

Spring Framework에서는 IoC Container가 어떻게 구현되고 있는지 궁금해서 작성해본 포스트인데 너무 힘들다... 일단 추상화가 너무 잘되어있어서 코드 흐름을 따라가기 힘들다. 구현체도 환경별로 구현되어 있어서 어떤 구현체 흐름을 따라가는지 알기가 힘들었다. 그래도 Spring 내부적으로 DI를 통한 IoC Container를 조금이나마 알 수 있는 시간이었다.

profile
Server Developer

0개의 댓글