SpringBoot是如何配置DispatcherServlet的

SpringMVC的核心入口就是DispatcherServlet,无论是通过web.xml 还是 JavaConfig的方式配置,都是要遵循Servlet规范的,即想要动态的添加配置一个Servlet本质上最终还是通过 ServletContext.addServlet(),虽然有了SpringMVC一般很少有需要直接编写Servlet,但是对于理解原理还是很必要的

配置Servlet的方法有许多方式:
1. web.xml静态配置servlet
2. ServletContainerInitializer编码动态注册
3. 注解@WebServlet (v3.0+)

SpringMVC: WebApplicationInitializer

Servlet3提供了ServletContainerInitializer接口来支持动态的注册Servlet/Filter/Listener

spring-web模块里的SpringServletContainerInitializer实现并抽象出新的接口:WebApplicationInitializer

所以,它的存在基本替代web.xml配置,在初始化的时候注册并配置容器上下文,官方doc的示例:

public class MyWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletCxt) {
        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();
        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

为了简化DispatcherServlet的基本千篇一律的配置,也提供了AbstractAnnotationConfigDispatcherServletInitializer直接继承实现对应的配置类列表相关方法即可

SpringBoot: ServletContextInitializer

SpringBoot没有选择hook到Servlet规范里的的生命周期,而是抽象出自己的接口来配置嵌入的容器,把Servlet/Filter/Listener这些变成Spring Bean一样去配置

ServletContainerInitializer 生命周期是归Servlet容器管理
ServletContextInitializer 他是Spring自己管理

SpringBoot应用一般都是embbed server,默认不会去走Servlet那一套,所以想配置Servlet就是通过ServletContextInitializer或者其子类来实现注册

源码的大概流程

  1. SpringApplication.run();

  2. ServletWebServerApplicationContext#onRefresh() -> createWebServer()

创建嵌入的servlet容器的时候(默认tomcat),new TomcatWebServer()构造函数里执行了初始化initialize(),也就是日志里比较标志性的一行 Tomcat initialized with port(s): 8080 (http)

  1. Tomcat#start() -> LifecycleBase#start()
    -> StandardService#startInternal() -> StandardEngine#startInternal() -> ContainerBase#startInternal()

然后提交了异步的FutureTask,就是startStopExecutor.submit(new StartChild(children[i])),TomcatEmbeddedContext#start()的时候会执行他的ServletContextInitializer列表

ServletWebServerApplicationContext里有一个实现的ServletContextInitializer逻辑:

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    registerApplicationScope(servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    // 这里就是完成各种Servlet Filter Listener的注册关键逻辑
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

getServletContextInitializerBeans()逻辑就是从BeanFactory里获取指定类型的Bean列表,当然这其中就包含了一个DispatcherServletRegistrationBean,这个Bean的配置是在DispatcherServletAutoConfiguration里配置的

总结

  1. DispatcherServletAutoConfiguration配置相应的Bean
  2. 内嵌容器start,然后会执行当然上下文里ServletContextInitializer类型的Bean完成初始化&配置

你可能感兴趣的:(SpringBoot是如何配置DispatcherServlet的)