본문 바로가기
백엔드/스프링 프레임워크 & 스프링 부트

IoC Container와 생명주기를 알아보자

by slowcloud_ 2025. 6. 11.

2025-06-13 스프링부트 컨텍스트 생성 순서 수정

tl;dr

  • BeanFactory와 ApplicationContext가 IoC Container이다.
  • ApplicationContext가 BeanFactory의 기능을 확장하고 있으며, 내부적으로 해당 인스턴스를 관리한다.
  • 아래에서는 상세한 설명, 그리고 컨텍스트의 생성 방식 등을 설명해두었다.

BeanFactory와 ApplicationContext

spring-framework의 코어 기능은 IoC Container이다. BeanFactory와 ApplicationContext 인터페이스를 기반으로 구현되어 있으며, ApplicationContext가 BeanFactory의 기능을 확장하고 있다. *

ApplicationContext는 내부적으로 BeanFactory 객체를 포함하고 관리하고 있으며, 기본적으로 DefaultListableBeanFactory를 사용한다. *

ApplicationContext는 BeanFactoryPostProcessor, BeanPostProcessor와 같은 빈 초기화 클래스를 내부적으로 생성하고 수행한다. 각 ApplicationContext마다, 내부적으로 기본적으로 사용하는 알맞은 빈 초기화 클래스를 직접 생성하고 등록한다. *

유명한 빈 초기화 클래스로는 @Configuration을 처리하는 BeanFactoryPostProcessor인 ConfigurationClassPostProcessor, @Autowired, @Inject 등을 처리하는 BeanPostProcessor인  AutowiredAnnotationBeanPostProcessor 등이 있다.

ConfigurationClassPostProcessor의 경우, @Configuration이나 @Component 등을 스캔해서 빈으로 등록하는 작업을 수행한다. @Configuration 클래스의 @ComponentScan 어노테이션을 감지하여 컴포넌트를 스캔하는 작업도 수행한다.

AnnotationConfigApplicationContext 등에서, 내부적으로  AnnotatedBeanDefinitionReader와 ClassPathBeanDefinitionScanner를 생성하며, 해당 클래스들의 내부 빈 등록 로직에서 AnnotationConfigUtils의 void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) 정적 메소드를 통해 ConfigurationClassPostProcessor와 같은 PostProcessor 클래스들을 등록하고 있다. *

 

DefaultListableBeanFactory는 BeanDefinitionRegistry 인터페이스의 registerBeanDefinition(String beanName, BeanDefinition beanDefinition)을 통해 빈을 등록할 수 있다. *

BeanDefinition은 주입할 빈의 정보, 메타데이터를 가지고 있으며, 빈 요청 시점에서 해당 정보를 바탕으로 객체를 생성한 후 반환한다. AnnotatedBeanDefinitionReader의 경우, 내부적으로 AnnotatedGenericBeanDefinition을 생성하여 컨텍스트에 등록한다. *

 

ApplicationContext 계열은 직접 생성해서 사용하면 된다.

 

서블릿과 WebApplicationContext

spring webmvc에서는 ApplicationContext 대신 WebApplicationContext를 사용한다.

spring webmvc에서는 서블릿 생명주기 내에서 WebApplicationContext를 생성하고 관리하는 작업을 수행해야 한다. 서블릿컨텍스트의 빈으로 등록하기 위해 web.xml을 작성하거나, WebApplicationInitializer를 상속해서 초기화 과정을 작성하면 된다. * **

WebApplicationContext는 ServletContext를 통해 Context hierarchy를 구성할 수 있다. DispatcherServlet의 부모 클래스인 FrameworkServlet에서 initWebApplicationContext() 메소드에서 WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)를 통해 서블릿 컨텍스트로부터 WebApplicationContext를 불러와 해러시를 구성하는 작업을 수행한다. * **

 

WebApplicationContext에서 register 또는 scan으로 빈 등록 시, 반드시 refresh 메소드를 실행해주어야 한다. ApplicationContext는 register 또는 scan 시 즉시 빈 등록 작업을 수행하지만, WebApplicationContext에서는 refresh 작업 수행 시 빈 등록 작업을 수행한다. * ** ***

스프링부트의 ServletWebServerApplicationContext

스프링부트에서는 @AutoConfiguration이 존재한다. @AutoConfiguration이 달린 클래스 내부에는 @Configuration들이 있으며, @Conditional 어노테이션들을 바탕으로 사용할 컨픽과 사용하지 않을 컨픽을 구분하여 빈으로 등록한다. 이들은 보통 META-INF/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 내부에 자신의 위치가 기록되어 있으며, 추후 AutoConfigurationImportSelector에 의해 호출된다. 또는 @AutoConfigurationPackage를 작성하여 추가로 작성한 @AutoConfiguration을 추가할 수 있다. 이는 @SpringBootApplication에 포함되어 있다. * **

스프링부트에서는 ServletWebServerApplicationContext라는 컨텍스트를 사용한다. 이는 내부적으로 웹서버를 초기화하고 실행하는 작업을 수행하며, ServletWebServerFactoryAutoConfiguration 내부에서 등록된 ServletWebServerFactory를 가져와 초기화하는 작업을 수행한다. Tomcat, Jetty, Undertow 3가지 서버를 지원하고 있다. *

 

스프링부트 내부 컨텍스트 생성 방식

스프링부트에서는 SpringApplication.run 메소드 내부에서 컨텍스트가 생성되고, 초기화되고, 반환된다. 아래는 run 메소드 실행 시 이루어지는 컨텍스트 관리 방식에 대해 포괄적으로 설명한 내용이다. 컨텍스트만을 빠르게 설명하기 위해 생략한 내용이 많다. *

  • createApplicationContext() 메소드를 통해 컨텍스트를 얻는다
    • DefaultApplicationContextFactory에서 컨텍스트를 생성하고 반환한다
      • 내부적으로 SpringFactoriesLoader에서 ApplicationContextFactory를 가져온다
        • 이는 META-INF/spirng.factories에 등록된 ServletWebServerApplicationContextFactory를 반환한다 
      • AnnotationConfigServletWebServerApplicationContext를 생성하고 반환한다
        • 생성 단계에서 내부적으로 AnnotatedBeanDefinitionReader와 ClassPathBeanDefinitionScanner를 생성한다.
          • 생성한 두 클래스 내부에서 AnnotationConfigUtils를 통해 ConfigurationClassPostProcessor와 같은 기본적인 BeanFactoryPostProcessor들을 생성하고 컨텍스트에 등록한다.
  • prepareContext 메소드를 실행한다.
    • 거의 끝자락에서 load를 실행한다.
      • 여기에서 @SpringBootApplication이 달린 클래스가 sources에 담겨 전해진다.
      • BeanDefinitionLoader의 도움으로 컨텍스트에 등록된다.
        • 좀 더 엄밀히는 등록이 "예정"된다. 내부 구현을 보면 즉시 빈 등록을 하는 대신 내부적으로 기록해두고, refresh가 수행될 때 빈 등록을 수행한다.
        • @SpringBootApplication 내부의 @Configuration, @ComponentScan 등의 어노테이션이 있어, 추후 refreshContext 단계에서 앱 초기화가 이루어진다.
  • refreshContext를 실행한다
    • 부모 클래스 ServletWebServerApplicationContext에서 웹서버를 생성한다.
      • ServletWebServerFactoryAutoConfiguration에서 ServletWebServerFactory로 등록된 빈을 가져온다.
      • WebServer를 생성하고 컨텍스트를 등록한다.
    • 등록했던 BeanFactoryPostProcessor들을 꺼내 BeanFactory초기화를 수행한다.
      • ConfigurationClassPostProcessor에서 초기화를 수행한다.
        • 내부적으로 ConfigurationClassParser를 생성한다.
          • @Configuration 내부의 @Bean들을 컨텍스트에 등록한다.
          • 내부적으로 DefferedImportSelectorHandler를 통해 Importer를 관리한다.
            • @Import(AutoConfigurationImportSelector.class)를 통해 스프링부트의 AutoConfiguration들을 등록한다.
              • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 안에 작성된 모든 AutoConfiguration을 읽고, @Conditional 등의 조건에 따라 내부의 configuration들 중 필요한 것들만을 가져오고 처리한다.
        • 내부적으로 ComponentScanAnnotationParser를 생성한다.
          • @ComponentScan 어노테이션을 기반으로 컴포넌트들을 스캔하고 빈으로 등록한다.
      • 초기화가 이루어진 빈들은 BeanDefinition의 메타데이터에 초기화를 수행한 포스트프로세서들에 대한 정보가 기록되며, 추후 refresh가 재호출되었을 경우 해당 데이터를 확인하여 중복 초기화를 방지한다.