在 SpringBoot 应用中,DispatcherServlet 是 Spring MVC 的核心组件,负责处理所有的 HTTP 请求。SpringBoot 默认内嵌了 Tomcat 作为 Servlet 容器,而 DispatcherServlet 是如何在 SpringBoot 启动过程中被加载并关联到内置 Tomcat 的呢?本文将围绕 SpringBoot 的启动过程,深入源码,详细解析这一关联机制。
1. SpringBoot 启动过程的核心阶段
SpringBoot 的启动过程可以分为以下几个核心阶段:
- 初始化 SpringApplication:加载配置、创建应用上下文。
- 刷新应用上下文(Refresh Context) :加载 Bean、执行自动配置、启动内嵌服务器(如 Tomcat)。
- 启动完成:调用
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);
}
}
DispatcherServletRegistrationBean是ServletContextInitializer的实现类,这样就和第5步对上了。
源码解析:
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 的关联流程:
- SpringBoot 启动:调用
SpringApplication.run()方法,初始化应用上下文。 - 创建内嵌 Tomcat:在
onRefresh()方法中,调用createWebServer()创建 Tomcat 实例。 - 注册 DispatcherServlet:通过
ServletContextInitializer将DispatcherServlet注册到 Tomcat 的ServletContext中。 - 启动 Tomcat:调用
Tomcat.start()方法启动 Tomcat,DispatcherServlet开始处理请求。
理解这一过程,有助于我们更好地掌握 SpringBoot 的启动机制,并在实际开发中更高效地调试和优化应用。
希望本文对你有所帮助!如果有任何问题,欢迎留言讨论。