你执行java -jar,一个Tomcat就静悄悄地起来了------这中间发生了什么?从main方法到Connector,我们来走一遍Tomcat的"复活"之路。
Spring Boot最让人惊叹的特性之一,就是它能将应用打包成一个独立的JAR包,直接通过java -jar运行,内嵌的Tomcat会自动启动。这个过程中,没有web.xml,没有独立的Tomcat安装,一切看起来像魔法。
但魔法背后是精密的工程设计。本文将从源码层面,带你走完Tomcat从"被引入"到"启动完成"的全程,看看Spring Boot是如何让Tomcat"嵌入"到应用中的。
一、从jar包到容器:内嵌Tomcat的宏观路径
在深入源码之前,我们先从宏观上理解整个流程。当你的Spring Boot应用启动时,内嵌Tomcat的诞生要经历三个阶段:

核心要点:Tomcat的创建并非在Spring Boot启动的瞬间完成,而是作为Spring容器刷新过程的一部分,在onRefresh阶段被触发。
二、自动配置:为Tomcat的诞生准备好"产房"
在Tomcat真正被创建之前,Spring Boot已经通过自动配置机制,准备好了创建Tomcat所需的工厂类。
2.1 ServletWebServerFactoryAutoConfiguration
在spring.factories中,有一个关键的自动配置类:ServletWebServerFactoryAutoConfiguration。这个类负责导入不同容器的工厂配置。

EmbeddedTomcat这个内部类的代码很关键:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 注入各种定制器
return factory;
}
}
条件注解的含义:
@ConditionalOnClass:只有类路径中存在Tomcat.class,这个配置才会生效------这就是为什么引入spring-boot-starter-web会自动引入tomcat依赖
@ConditionalOnMissingBean:如果用户没有自定义ServletWebServerFactory,才使用默认的
三、核心时机:onRefresh与createWebServer
有了工厂类之后,真正的创建动作发生在什么时候?答案是Spring容器刷新的onRefresh阶段。
3.1 ServletWebServerApplicationContext
Spring Boot为Web应用准备了一个特殊的ApplicationContext实现------ServletWebServerApplicationContext。它的onRefresh方法中,调用了关键的createWebServer方法:

3.2 获取工厂类
在getWebServerFactory()方法中,Spring会从容器中查找类型为ServletWebServerFactory的Bean:
java
protected ServletWebServerFactory getWebServerFactory() {
// 获取所有ServletWebServerFactory类型的Bean名称
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
// 必须有且仅有一个,否则抛出异常
if (beanNames.length == 0) {
throw new ApplicationContextException("...");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("...");
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
这种"必须有且仅有一个"的设计,体现了Spring Boot的约定优于配置原则。如果你不小心注入了多个工厂Bean,启动时会直接报错,避免了不确定行为。
四、Tomcat的诞生:从工厂到实例
有了工厂类,接下来就是创建真正的Tomcat实例。
4.1 TomcatServletWebServerFactory.getWebServer
TomcatServletWebServerFactory的getWebServer方法,是Tomcat诞生的地方:

4.2 构造Tomcat实例
new Tomcat()创建的是一个"骨架"实例,此时还没有启动任何组件。随后,工厂会进行一系列配置:
设置baseDir:临时目录,用于存放解压的JSP等资源
创建Connector:默认创建HTTP/1.1连接器,监听8080端口
配置Host:创建StandardHost实例,设置appBase等
应用定制器:执行用户注册的各种TomcatConnectorCustomizer、TomcatContextCustomizer等
4.3 启动Tomcat
真正的启动发生在TomcatWebServer的构造器中:
java
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
this.tomcat = tomcat;
initialize();
}
private void initialize() {
// ... 省略前置处理
start(); // 调用tomcat.start
// ... 后置处理
}
tomcat.start()会触发Tomcat自身的生命周期:从Server开始,依次启动Service、Engine、Host、Context,最后启动Connector,开始监听端口。
五、Tomcat自身的启动流程
当调用tomcat.start()时,Tomcat内部的组件树开始启动。Tomcat的架构是分层的:

启动顺序:按照组件的树形结构,从顶层到底层,依次调用生命周期方法。Connector的启动会打开Socket端口,开始接受HTTP请求。
六、扩展与定制:如何替换为Jetty/Undertow?
理解了Tomcat的启动原理,替换其他容器就很简单了。Spring Boot的设计让容器完全可插拔。
6.1 排除Tomcat,引入Jetty
java
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
6.2 原理分析
当排除Tomcat后,TomcatServletWebServerFactory的创建条件@ConditionalOnClass(Tomcat.class)就不再满足,因为Tomcat的类不存在了。同时,引入的spring-boot-starter-jetty会带来Jetty的依赖和对应的自动配置,JettyServletWebServerFactory就会生效。
整个过程完全自动化,开发者只需要改变依赖,Spring Boot负责处理剩下的逻辑。
七、总结:从jar到web服务器的完整链条
回顾整个流程,Spring Boot启动内嵌Tomcat的核心路径如下:
java
SpringApplication.run()
↓
调用 run() 方法(SpringApplication.java)
↓
创建 ApplicationContext(AnnotationConfigServletWebServerApplicationContext)
↓
refreshContext() → 调用 onRefresh()
↓
onRefresh() → 调用 createWebServer()
↓
调用 ServletWebServerApplicationContext.createWebServer()
↓
通过 WebServerFactory 获取 TomcatServletWebServerFactory
↓
factory.getWebServer(ServletContextInitializer...)
↓
new Tomcat().getServer()
↓
Tomcat.start()
↓
内嵌 Tomcat Server 启动完成
关键设计思想:
分离接口与实现:ServletWebServerFactory定义创建Web服务器的规范,具体实现由各容器提供
条件化配置:通过@ConditionalOnClass等注解,根据类路径决定使用哪个容器
单一工厂原则:容器中只能有一个ServletWebServerFactory Bean,避免冲突
生命周期钩子:通过onRefresh将容器启动与Web服务器启动有机结合
理解了这些原理,你不仅能回答"Spring Boot怎么启动Tomcat",还能在遇到容器相关问题时快速定位------比如端口冲突、Connector定制失败、容器切换异常等。