揭秘 Spring Boot 启动:DispatcherServlet 是如何绑定到 Tomcat 容器的?

341 阅读3分钟

在 SpringBoot 应用中,DispatcherServlet 是 Spring MVC 的核心组件,负责处理所有的 HTTP 请求。SpringBoot 默认内嵌了 Tomcat 作为 Servlet 容器,而 DispatcherServlet 是如何在 SpringBoot 启动过程中被加载并关联到内置 Tomcat 的呢?本文将围绕 SpringBoot 的启动过程,深入源码,详细解析这一关联机制。

image.png

1. SpringBoot 启动过程的核心阶段

SpringBoot 的启动过程可以分为以下几个核心阶段:

  1. 初始化 SpringApplication:加载配置、创建应用上下文。
  2. 刷新应用上下文(Refresh Context) :加载 Bean、执行自动配置、启动内嵌服务器(如 Tomcat)。
  3. 启动完成:调用 ApplicationRunner 或 CommandLineRunner,完成启动。

本文将聚焦于第 2 阶段(刷新应用上下文),详细分析 DispatcherServlet 是如何被加载并关联到内置 Tomcat 的。

2. 刷新应用上下文(Refresh Context)

在 SpringBoot 启动过程中,refreshContext() 是核心方法之一,负责初始化应用上下文并启动内嵌服务器。以下是简化后的源码:

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
}

refresh() 方法最终会调用 AbstractApplicationContext.refresh(),这是 Spring 框架的核心方法,负责初始化应用上下文。

3. 初始化 Servlet Web 应用上下文

对于基于 Servlet 的 Web 应用,SpringBoot 会创建 AnnotationConfigServletWebServerApplicationContext 作为应用上下文。在 refresh() 方法中,会调用 onRefresh() 方法,这是启动内嵌服务器的关键。

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer(); // 创建内嵌 Web 服务器
    } catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

4. 创建内嵌 Web 服务器(createWebServer)

在 createWebServer() 方法中,SpringBoot 会根据配置创建内嵌的 Web 服务器(如 Tomcat),并将 DispatcherServlet 注册到 Tomcat 中。

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer()); // 获取 Web 服务器
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        } catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

源码解析:

  • getWebServerFactory():获取 ServletWebServerFactory 实例,默认是 TomcatServletWebServerFactory
  • getSelfInitializer():返回一个 ServletContextInitializer,用于将 DispatcherServlet 注册到 Tomcat 中。
  • factory.getWebServer():创建并启动内嵌的 Tomcat 服务器。

5. 注册 DispatcherServlet

在 getSelfInitializer() 方法中,SpringBoot 会返回一个 ServletContextInitializer,用于将 DispatcherServlet 注册到 Tomcat 中。

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    registerApplicationScope(servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext); // 注册 DispatcherServlet
    }
}

源码解析:

  • getServletContextInitializerBeans():获取所有 ServletContextInitializer 类型的 Bean,包括 DispatcherServletRegistrationBean
  • beans.onStartup(servletContext):调用 ServletContextInitializer 的 onStartup() 方法,将 DispatcherServlet 注册到 Tomcat 的 ServletContext 中。

6. DispatcherServlet 的注册

DispatcherServlet 的注册是通过 DispatcherServletRegistrationBean 完成的。以下是 DispatcherServletRegistrationBean 的关键源码:

public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet> {
    public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
        super(servlet, path);
        setLoadOnStartup(-1);
    }
}

DispatcherServletRegistrationBeanServletContextInitializer的实现类,这样就和第5步对上了。 截屏2025-02-18 23.37.18.png

源码解析:

  • super(servlet, path):将 DispatcherServlet 注册到指定的路径(默认是 /)。
  • setLoadOnStartup(-1):设置 DispatcherServlet 的启动顺序。

7. Tomcat 启动与 DispatcherServlet 的加载

在 createWebServer() 方法中,SpringBoot 会调用 TomcatServletWebServerFactory.getWebServer() 方法创建并启动 Tomcat。

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    // 配置 Tomcat
    return new TomcatWebServer(tomcat, true);
}

在 Tomcat 启动时,会调用 ServletContextInitializer.onStartup() 方法,将 DispatcherServlet 注册到 Tomcat 的 ServletContext 中。

8. 总结

通过源码分析,我们可以看到 SpringBoot 启动过程中 DispatcherServlet 的加载与 Tomcat 的关联流程:

  1. SpringBoot 启动:调用 SpringApplication.run() 方法,初始化应用上下文。
  2. 创建内嵌 Tomcat:在 onRefresh() 方法中,调用 createWebServer() 创建 Tomcat 实例。
  3. 注册 DispatcherServlet:通过 ServletContextInitializer 将 DispatcherServlet 注册到 Tomcat 的 ServletContext 中。
  4. 启动 Tomcat:调用 Tomcat.start() 方法启动 Tomcat,DispatcherServlet 开始处理请求。

理解这一过程,有助于我们更好地掌握 SpringBoot 的启动机制,并在实际开发中更高效地调试和优化应用。

希望本文对你有所帮助!如果有任何问题,欢迎留言讨论。