본문 바로가기
Framework & Lib & API/스프링

스프링 빈 생성 과정 분석 [4] - 디버깅 참고 자료

by 코딩공장공장장 2024. 8. 7.

[스프링 빈 생성 과정 분석 시리즈]

 

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을 토대로 빈을 생성하여 가져오는 로직을 확인하였다.






반응형