- 스프링은 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 구현체가 수행하는 것이다.
- 즉, 서블릿 컨테이너가 생성한 ServletContext를 SpringServletContainerInitializer가 받고, ServletContext를 초기화하는 실제 작업은 WebApplicationInitializer 구현체가 수행하는 것이다.
- 동작 매커니즘
- spring-web 모듈 JAR가 클래스패스에 있으면, SpringServletContainerInitializer는 서블릿 컨테이너가 시작될 때 로드되고 인스턴스화되며 onStartup 메소드가 호출된다.
- JAR 서비스 API의 ServiceLoader.load(Class) 메소드가 spring-web 모듈의 /META-INF/services/jakarta.servlet.ServletContainerInitializer 파일을 감지함으로써 발생하는 것이다.
- onStartup 메소드는 WebApplicationInitializer 구현체들을 찾아서 초기화하는 역할을 한다.
- 만약 클래스패스에서 WebApplicationInitializer 구현체를 찾을 수 없다면, onStartup 메서드는 아무 작업도 수행하지 않는다.
- 하나 이상의 WebApplicationInitializer 타입이 감지되면, 이 구현체들은 인스턴스화된다. 그런 다음, 각 WebApplicationInitializer 인스턴스들의 onStartup(ServletContext) 메서드가 호출된다.
- spring-web 모듈 JAR가 클래스패스에 있으면, SpringServletContainerInitializer는 서블릿 컨테이너가 시작될 때 로드되고 인스턴스화되며 onStartup 메소드가 호출된다.
WebApplicationInitializer
public interface WebApplicationInitializer
{
void onStartup(ServletContext servletContext) throws ServletException;
}
- WebApplicationInitializer는 ServletContext를 프로그래밍 방식으로 구성하기 위해 구현해야 하는 인터페이스이다.
- WebApplicationInitializer 인터페이스를 구현한 클래스는 서블릿 컨테이너 실행 시 SpringServletContainerInitializer에 의해 자동으로 감지되고 실행된다.
- WebApplicationInitializer 인터페이스를 구현한 클래스는 서블릿 컨테이너 실행 시 SpringServletContainerInitializer에 의해 자동으로 감지되고 실행된다.
- SpringServletContainerInitializer, WebApplicationInitializer 구현에서 Spring MVC와 무조건적으로 결합해야 할 필요는 없다. WebApplicationInitializer에는 Spring MVC 요소 이외에 서블릿, 필터, 리스너, 각종 필터를 등록 할 수 있다.
ApplicationContext 설정 및 DispatcherServlet 등록 예시
- ApplicationContext 설정 및 DispatcherServlet 등록을 위해서 두 가지 방식을 사용할 수 있다.
- WebApplicationInitializer 인터페이스를 직접 구현(implements)
- WebApplicationInitializer 인터페이스를 구현한 추상 클래스인 AbstractAnnotationConfigDispatcherServletInitializer를 구현(extends)
- 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의 컴포넌트 스캔이 실행된다.
- Root WebApplicationContext
- ContextLoaderListener
- ContextLoaderListener는 스프링의 Root WebApplicationContext를 시작하고 종료하는 데 사용되는 부트스트랩 리스너이다. Root WebApplicationContext를 리스너로 관리해야 ServletContext의 종료 이벤트를 받았을 때 자원을 적절히 반환할 수 있다.
- ContextLoaderListener는 ServletContextListener를 구현하고, ContextLoader를 상속한다. ContextLoaderListener는 ServletContextListener를 구현하여 ServletConetxt의 시작 및 종료 이벤트를 처리할 수 있다.
- => Root WebApplicationContext와 DispatcherServlet의 WebApplicationContext의 초기화 시점
- 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-컨테이너