Home > Spring > (Spring) - 스프링이 제공하는 ServletContainerInitializer

(Spring) - 스프링이 제공하는 ServletContainerInitializer
spring

web.xml을 대체하는 ServletContainerInitializer 먼저 읽고 오자.

  • 스프링은 ServletContainerInitializer 인터페이스를 구현한 클래스로 SpringServletContainerInitializer를 제공하며, 개발자가 실제 구현해야 하는 것은 WebApplicationInitializer이다.


SpringServletContainerInitializer

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer 
    {
        public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, 
                              ServletContext servletContext) 
                              throws ServletException 
        {
            ...
            WebApplicationInitializer initializer = (WebApplicationInitializer) var4.next();
            initializer.onStartup(servletContext);
            ...
        }
    }
  • SpringServletContainerInitializer는 WebApplicationInitializer 구현체를 인스턴스화하고 ServletContext를 위임하는 역할을 한다.
    • 즉, 서블릿 컨테이너가 생성한 ServletContext를 SpringServletContainerInitializer가 받고, ServletContext를 초기화하는 실제 작업은 WebApplicationInitializer 구현체가 수행하는 것이다.

  • 동작 매커니즘
    1. spring-web 모듈 JAR가 클래스패스에 있으면, SpringServletContainerInitializer는 서블릿 컨테이너가 시작될 때 로드되고 인스턴스화되며 onStartup 메소드가 호출된다.
      • JAR 서비스 API의 ServiceLoader.load(Class) 메소드가 spring-web 모듈의 /META-INF/services/jakarta.servlet.ServletContainerInitializer 파일을 감지함으로써 발생하는 것이다.
    2. onStartup 메소드는 WebApplicationInitializer 구현체들을 찾아서 초기화하는 역할을 한다.
      • 만약 클래스패스에서 WebApplicationInitializer 구현체를 찾을 수 없다면, onStartup 메서드는 아무 작업도 수행하지 않는다.
    3. 하나 이상의 WebApplicationInitializer 타입이 감지되면, 이 구현체들은 인스턴스화된다. 그런 다음, 각 WebApplicationInitializer 인스턴스들의 onStartup(ServletContext) 메서드가 호출된다.

WebApplicationInitializer

public interface WebApplicationInitializer 
{    
    void onStartup(ServletContext servletContext) throws ServletException;
}
  • WebApplicationInitializer는 ServletContext를 프로그래밍 방식으로 구성하기 위해 구현해야 하는 인터페이스이다.
    • WebApplicationInitializer 인터페이스를 구현한 클래스는 서블릿 컨테이너 실행 시 SpringServletContainerInitializer에 의해 자동으로 감지되고 실행된다.

  • SpringServletContainerInitializer, WebApplicationInitializer 구현에서 Spring MVC와 무조건적으로 결합해야 할 필요는 없다. WebApplicationInitializer에는 Spring MVC 요소 이외에 서블릿, 필터, 리스너, 각종 필터를 등록 할 수 있다.

ApplicationContext 설정 및 DispatcherServlet 등록 예시

  • ApplicationContext 설정 및 DispatcherServlet 등록을 위해서 두 가지 방식을 사용할 수 있다.
    1. WebApplicationInitializer 인터페이스를 직접 구현(implements)
    2. WebApplicationInitializer 인터페이스를 구현한 추상 클래스인 AbstractAnnotationConfigDispatcherServletInitializer를 구현(extends)
  1. WebApplicationInitializer 직접 구현
    public class MyWebAppInitializer implements WebApplicationInitializer 
    {
         @Override
         public void onStartup(ServletContext servletContext) 
         {
             // Root WebApplicationContext 생성
             AnnotationConfigWebApplicationContext rootContext 
                 = new AnnotationConfigWebApplicationContext();
             // Root WebApplicationContext 설정 클래스 등록
             rootContext.register(AppConfig.class);
    
             // ContextLoaderListener를 등록하여 Root WebApplicationContext를 초기화
             servletContext.addListener(new ContextLoaderListener(rootContext));
    
             // DispatcherServlet의 WebApplicationContext 생성
             AnnotationConfigWebApplicationContext dispatcherContext 
                 = new AnnotationConfigWebApplicationContext();
             // DispatcherServlet WebApplicationContext 설정 클래스 등록 
             dispatcherContext.register(DispatcherConfig.class);
    
             // DispatcherServlet 등록 및 매핑
             ServletRegistration.Dynamic dispatcher 
                 = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
             dispatcher.addMapping("/"); 
             dispatcher.setLoadOnStartup(1); // DispatcherServlet이 웹 애플리케이션 시작 시 로드되도록 설정 
         }
     }
    
    • => Root WebApplicationContext와 DispatcherServlet의 WebApplicationContext의 초기화 시점
      • Root WebApplicationContext
        • ContextLoaderListener에 의해 초기화되는데, ContextLoaderListener의 contextInitialized 메소드가 호출되면서 초기화된다.
        • 또한, Root WebApplicationContext가 초기화될 때 AppConfig.class의 컴포넌트 스캔이 실행된다.
      • DispatcherServlet의 WebApplicationContext
        • DispatcherServlet에 의해 초기화되는데, DispatcherServlet이 초기화될 때 initWebApplicationContext 메소드가 호출되면서 초기화된다.
        • 또한, DispatcherServlet의 WebApplicationContext가 초기화될 때 DispatcherConfig.class의 컴포넌트 스캔이 실행된다.
    • ContextLoaderListener
      • ContextLoaderListener는 스프링의 Root WebApplicationContext를 시작하고 종료하는 데 사용되는 부트스트랩 리스너이다. Root WebApplicationContext를 리스너로 관리해야 ServletContext의 종료 이벤트를 받았을 때 자원을 적절히 반환할 수 있다.
      • ContextLoaderListener는 ServletContextListener를 구현하고, ContextLoader를 상속한다. ContextLoaderListener는 ServletContextListener를 구현하여 ServletConetxt의 시작 및 종료 이벤트를 처리할 수 있다.
  2. AbstractAnnotationConfigDispatcherServletInitializer 구현
    public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
    {
         @Override
         protected Class<?>[] getRootConfigClasses() 
         {
             // Root WebApplicationContext 설정 클래스 등록
             return new Class<?>[] { AppConfig.class };
         }
    
         @Override
         protected Class<?>[] getServletConfigClasses() 
         {
             // DispatcherServlet WebApplicationContext 설정 클래스 등록 
             return new Class<?>[] { DispatcherConfig.class };
         }
    
         @Override
         protected String[] getServletMappings() 
         {
             // DispatcherServlet 매핑
             return new String[] { "/" };
         }
     }
    

참고 자료

https://tomcat.apache.org/tomcat-7.0-doc/servletapi/javax/servlet/ServletContainerInitializer.html https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/ServletContext.html https://tomcat.apache.org/tomcat-7.0-doc/servletapi/javax/servlet/ServletRegistration.Dynamic.html https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/SpringServletContainerInitializer.html https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/WebApplicationInitializer.html https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/ContextLoaderListener.html https://kimcoder.tistory.com/511 https://recordsoflife.tistory.com/490 https://escapefromcoding.tistory.com/174 https://offbyone.tistory.com/215 https://nhs0912.tistory.com/81 https://blog.naver.com/kitepc/221314687808 https://velog.io/@rolroralra/Chapter0.-Servlet-컨테이너-Spring-컨테이너