Spring Boot 内嵌 Tomcat 的启动流程,本质上就是把“手动部署WAR包到外部Tomcat”这件事,变成通过Java代码在main方法里一键启动。
整个流程拆成三大阶段,用最通俗的话讲透:
阶段一:SpringApplication 启动,触发自动配置
当你点击运行@SpringBootApplication的main方法时:
SpringApplication.run()开始执行核心刷新(refresh)容器。Spring Boot的自动配置机制会扫描类路径,发现你依赖了
spring-boot-starter-web,这会触发ServletWebServerFactoryAutoConfiguration配置类。这个配置类会做一件事:创建一个内嵌Web服务器的工厂。
默认情况下,因为Tomcat的依赖存在,它会创建
TomcatServletWebServerFactory。这个工厂就是专门负责“生产”Tomcat服务器的。
阶段二:创建并配置 Tomcat 实例
这个阶段是整个流程的肉,它发生在refresh上下文时的onRefresh()方法里,具体步骤如下:
工厂开始构建:
TomcatServletWebServerFactory的getWebServer()方法被调用,此时会:实例化一个原生的
org.apache.catalina.startup.Tomcat对象。设置基础目录(
basedir),通常是操作系统的临时目录。
配置核心组件(Engine/Host/Context):
创建Engine(引擎),默认名字叫
"Tomcat"。创建Host(虚拟主机),默认是
localhost,并设置应用根目录。最关键的一步:创建Context(上下文)。这个
Context代表你的整个Spring Boot应用,它的路径通常是空字符串""(即根路径)。
添加Servlet(即DispatcherServlet):
Spring Boot会创建一个
DispatcherServlet的Bean。然后将这个Servlet动态添加到刚才创建的Context中,并映射到
"/"(即所有请求)。注意:这里的添加并不是通过
web.xml,而是通过编程方式调用Tomcat.addServlet()和Context.addServletMapping()。
设置端口和连接器:
读取你配置文件里的
server.port(默认8080),设置到Tomcat的Connector(连接器)上。默认使用NIO协议(
org.apache.coyote.http11.Http11NioProtocol)。
此时,Tomcat实例已经构建完毕,但还没有启动,只是准备好了。
阶段三:启动 Tomcat 并发布 ServletContext
这是最后的点火动作:
触发启动:在
refresh容器的最后一步(finishRefresh),会调用TomcatWebServer的start()方法。内部启动链:
调用
Tomcat.start()-> 这会启动所有组件(Connector、Engine、Host、Context)。连接器(Connector)开始监听指定端口,准备接收HTTP请求。
同时,会触发
ServletContextInitializer回调,执行你所有的Filter、Listener的初始化。
发布事件:启动成功后,Spring Boot会发布
ServletWebServerInitializedEvent事件,表示内嵌服务器已就绪。阻塞等待:最后,
TomcatWebServer会启动一个非守护线程(keepAlive),让主线程阻塞,保证Tomcat一直运行,等待请求进来。
核心时序图
与外部Tomcat部署的关键区别(面试加分点)
| 对比维度 | 外部Tomcat(传统WAR) | Spring Boot 内嵌Tomcat |
|---|---|---|
| 启动入口 | 由Tomcat脚本(catalina.sh)触发 | 由Java的main()方法触发 |
| Servlet容器 | 独立进程,部署多个WAR | 嵌入在JVM中,随应用启动和停止 |
| Context创建方式 | 解析web.xml或注解扫描 | 编程式创建Context,并手动注册DispatcherServlet |
| 类加载器 | 复杂的委派机制(Common/Shared/Webapp) | 相对简单,直接使用应用类加载器(AppClassLoader) |
| 适用场景 | 多应用共享Tomcat | 微服务、云原生、独立运行 |