Spring Boot Web容器初始化方式

Spring Boot容器另类的初始化过程

  • 对于Spring Boot应用来说,它并未使用SpringServletContainerInitializer来进行容器的初始化,而是使用了TomcatStarter进行的。
  • TomcatStarter存在三点因素使得它无法通过SPI机制进行初始化
    • 它没有不带参数的构造方法
    • 它的声明并非public
    • 其所在的jar包并没有META-INF/services目录
  • 所以TomcatStarter并非通过SPI机制进行的查找与实例化
  • 本质上,TomcatStarter是通过Spring Boot框架new出来的
  • 与SpringServletContainerInitializer类似,TomcatStarter在容器的初始化过程中也是扮演一个委托或是代理的角色,真正执行的初始化动作实际上是由它所持有的ServletContextInitializer的onStartup方法。

具体代码

  1. ServletContextInitializer
    package org.springframework.boot.web.servlet;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    import org.springframework.web.SpringServletContainerInitializer;
    import org.springframework.web.WebApplicationInitializer;
    
    /**
    * Interface used to configure a Servlet 3.0+ {@link ServletContext context}
    * programmatically. Unlike {@link WebApplicationInitializer}, classes that implement this
    * interface (and do not implement {@link WebApplicationInitializer}) will not be
    * detected by {@link SpringServletContainerInitializer} and hence will not be
    * automatically bootstrapped by the Servlet container.
    * 

    * This interface is primarily designed to allow {@link ServletContextInitializer}s to be * managed by Spring and not the Servlet container. *

    * For configuration examples see {@link WebApplicationInitializer}. * * @author Phillip Webb * @since 1.4.0 * @see WebApplicationInitializer */ @FunctionalInterface public interface ServletContextInitializer { /** * Configure the given {@link ServletContext} with any servlets, filters, listeners * context-params and attributes necessary for initialization. * @param servletContext the {@code ServletContext} to initialize * @throws ServletException if any call against the given {@code ServletContext} * throws a {@code ServletException} */ void onStartup(ServletContext servletContext) throws ServletException; }

  2. TomcatStarter
    /**
    * {@link ServletContainerInitializer} used to trigger {@link ServletContextInitializer
    * ServletContextInitializers} and track startup errors.
    *
    * @author Phillip Webb
    * @author Andy Wilkinson
    */
    class TomcatStarter implements ServletContainerInitializer {
    
        private static final Log logger = LogFactory.getLog(TomcatStarter.class);
    
        private final ServletContextInitializer[] initializers;
    
        private volatile Exception startUpException;
    
        TomcatStarter(ServletContextInitializer[] initializers) {
            this.initializers = initializers;
        }
    
        @Override
        public void onStartup(Set> classes, ServletContext servletContext) throws ServletException {
            try {
                for (ServletContextInitializer initializer : this.initializers) {
                    initializer.onStartup(servletContext);
                }
            }
            catch (Exception ex) {
                this.startUpException = ex;
                // Prevent Tomcat from logging and re-throwing when we know we can
                // deal with it in the main thread, but log for information here.
                if (logger.isErrorEnabled()) {
                    logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
                            + ex.getMessage());
                }
            }
        }
    
        Exception getStartUpException() {
            return this.startUpException;
        }
    
    }
    
  3. 最终会通过TomcatStarter这个委托者,获取到所有的ServletContextInitializer实例数组,然后在onStartup方法中循环调用onStartup完成初始化的配置。

和传统Spring MVC之间的对应关系

  1. SpringServletContainerInitializer对应于TomcatStarter
  2. WebApplicationInitializer对应于ServletContextInitializer

为什么不统一

当我们运行启动命令java -jar的时候是不会调用javax.servlet.ServletContainerInitializer这个接口的

关于传统Spring MVC的web容器初始化方式可以参考Spring MVC容器初始化方式这篇文章

你可能感兴趣的:(Spring Boot Web容器初始化方式)