티스토리 뷰

https://github.com/woowacourse/jwp-dashboard-mvc

 

GitHub - woowacourse/jwp-dashboard-mvc

Contribute to woowacourse/jwp-dashboard-mvc development by creating an account on GitHub.

github.com

미션을 진행하면서 DispatcherServletInitializer를 명시적으로 실행하지 않았음에도 불구하고 DispatcherServlet이 자동으로 등록되는 이유에 대해 궁금하여 조사하려고 합니다


DispatcherServletInitializer 코드 확인

 

public class DispatcherServletInitializer implements WebApplicationInitializer {
    ...
}
public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {
    ...
    }
}

코드를 따라 들어가다보면 ServletContainerInitializer 가 나오는데 무엇일까?


ServletContainerInitializer 란?

ServletContainerInitializer
Interface which allows a library/runtime to be notified of a web application's startup phase and perform any required programmatic registration of servlets, filters, and listeners in response to it.

 

과거에는 web.xml 파일을 사용하여 Servlet Container에 서블릿, 필터 등의 정보를 설정해야 했습니다.
그러나 ServletContainerInitializer가 나오면서 이러한 설정을 더 이상 XML 파일에 의존하지 않고 자바 코드로 처리할 수 있게 되었습니다.

그리고 이 클래스의 이름을 /META-INF/services/javax.servlet.ServletContainerInitializer 라는 텍스트 파일에 기재해야 합니다.

이렇게 하면 XML 파일을 사용하지 않고도 서블릿 컨텍스트를 설정할 수 있습니다.


HandlesTypes 란?

HandlesTypes
The classes in which a ServletContainerInitializer has expressed interest. If an implementation of ServletContainerInitializer specifies this annotation, the Servlet container must pass the Set of application classes that extend, implement, or have been annotated with the class types listed by this annotation to the ServletContainerInitializer.onStartup(java.util.Set>, javax.servlet.ServletContext) method of the ServletContainerInitializer (if no matching classes are found, null must be passed instead)

 

Set<Class<?>>객체를 통해  javax.servlet.annotation.HandlesTypes에 지정한 클래스 객체를 갖고 올 수 있습니다.

 

즉, DispatcherServletInitializer가 ServletContainerInitializer를 통해 onStartup 메서드가 호출된 것은 이해했습니다. 

그렇다면 톰캣은 어떻게 ServletContainerInitializer를 실행시키는 걸까요?


ContextConfig.webConfig();

protected void webConfig() {
    ...
    // Step 3. Look for ServletContainerInitializer implementations
    if (ok) {
        processServletContainerInitializers();
    }
    ...
}

// org.apache.catalina.startup.ContextConfig

processServletContainerInitializers 에서 META-INF/services/javax.servlet.ServletContainerInitializer 파일을 클래스로더 리소스에서 찾는다.


StandardContext.startInternal();

@Override
protected synchronized void startInternal() throws LifecycleException {
    ...
    // Call ServletContainerInitializers
    for (Map.Entry<ServletContainerInitializer,Set<Class<?>>> entry : initializers.entrySet()) {
        try {
            entry.getKey().onStartup(entry.getValue(), getServletContext());
        } catch (ServletException e) {
            log.error(sm.getString("standardContext.sciFail"), e);
            ok = false;
            break;
        }
    } 
    ...
}

// org.apache.catalina.core.StandardContext

이후 ServletContainerInitializer들의 onStartup이 호출되고 있습니다.

 

마무리

  • 결국 getServer().start()를 호출하면 LifecycleBase의 start()가 호출되는데, 
    start() 메소드 내부적으로는 다시 StandardContext에서 startInternal()을 호출합니다.
  • StandardContext 에서 fireLifecycleEvent() 메소드를 호출 합니다.
  • ContextConfig webConfig() 메소드를 호출 합니다.
  • 다시 StandardContext에서 startInternal 에서 ServletContainerInitializer를 onStartup 합니다.

https://medium.com/chequer/tomcat-spring-bootstrapping-sequence-2%ED%8E%B8-spring-e19705529132

https://lordofkangs.tistory.com/294

https://ehdvudee.tistory.com/47