前言

Spring Boot 的目标是构建“非常容易创建、独立、产品级别的基于Spring的应用”。这些应用是“立即可运行的”。在这个过程中,完全没有代码生成,不需要配置任何特殊的XML配置,为了这个目标,Spring Boot 在 Spring 4.0 框架之上提供了很多特性,帮助应用以“约定优于配置”“开箱即用”的方式来启动应用并运行上下文。

Spring Boot 同样改变了一个传统的 Web 应用服务的启动流程和部署方式。通过自动配置机制,Spring Boot 提供了一个嵌入式的运行时容器环境,并使用代码注解的方式在代码中将 URL 服务地址映射到 Controller 的方法完成服务映射。

所以,开发者不再需要关心传统容器(如 Tomcat)中web.xml的配置,同时实现容器的具体技术都是可替换及可更改的,这些技术以插件化的Starter组件方式在运行时加载到 Spring 容器中。

img

什么是嵌入式容器

嵌入式容器是指将Servlet容器(如Tomcat、Jetty、Undertow)直接集成到应用程序中,而不是作为独立的外部服务器运行。这种设计使得应用程序可以作为一个独立的单元运行,无需额外安装和配置Web服务器。

优势

  • 简化部署:应用程序打包为可执行的JAR文件,包含所有依赖和运行时环境,部署时只需运行JAR文件即可。
  • 开发便捷:无需配置外部服务器,开发者可以专注于业务逻辑,快速启动和测试应用。
  • 灵活性:支持多种容器(Tomcat、Jetty、Undertow),开发者可以根据需求自由切换。
  • 适合云原生:嵌入式容器轻量且易于扩展,非常适合微服务和云原生应用场景。

Servlet 容器:管理、运⾏ Servlet 组件(Servlet、Filter、Listener)的环境,⼀般指服务器。

Spring Boot默认使用Tomcat作为嵌入式Servlet容器,但也支持Jetty和Undertow。开发者可以通过简单的依赖配置切换容器。

其对应的 Java Web 容器发展至今也分为 Servlet Web 容器和 Reactive Web 容器,前者的使用率大概占比是百分之九十左右,其具体的实现有 TomcatJettyUndertow;而后者它的默认实现为 Netty Web Server

Servlet 规范是 Java Web 开发的核心标准,由 Java Community Process(JCP)制定,用于定义 Web 容器与 Servlet 组件之间的交互规则。以下是 Tomcat、Jetty、Undertow 对 Servlet 规范的兼容情况对比:

容器名称 最新稳定版本 支持的最高 Servlet 规范 关键特性
Tomcat 10.1.x(2023 年) Servlet 5.0(JSP 3.0) - 最广泛使用的 Java Web 容器 - 轻量级、高稳定性 - 完整支持 JSP 与 EL 表达式 - 适用于中小型企业应用
Jetty 12.0.x(2023 年) Servlet 5.0(WebSocket 2.0) - 高性能异步处理能力 - 支持嵌入式部署(如 Spring Boot) - 轻量级架构,启动速度快 - 对 WebSocket 支持尤为出色
Undertow 2.3.x(2023 年) Servlet 4.0(WebSockets 1.1) - Red Hat 开发的高性能容器 - 基于 NIO 的非阻塞模型 - 内存占用低,适合微服务场景 - 与 Spring Boot 默认集成度高

其中的容器如何选择:

  1. Tomcat:兼容性优先
    • 若应用依赖 JSP 或需要兼容旧版框架(如 Struts 2),Tomcat 是首选。
    • Spring Boot 默认使用 Tomcat,适合快速搭建传统 Web 应用。
  2. Jetty:异步与 WebSocket 场景
    • 实时通信系统(如在线聊天、股票行情)推荐 Jetty,其 WebSocket 性能优于 Tomcat。
    • 嵌入式服务(如工具类应用)因轻量级特性更适合 Jetty。
  3. Undertow:高性能与微服务
    • 高并发、低延迟场景(如 API 网关、实时数据处理)优先选择 Undertow。
    • 与 Spring Boot 集成时,可通过配置 application.properties 切换:

自动配置嵌入式容器的原理

核心机制

Spring Boot 自动配置嵌入式容器的核心是通过 条件注解工厂模式 实现的。这一机制遵循 “约定优于配置” 的原则,使开发者无需手动配置服务器。

关键组件

  • ServletWebServerFactoryAutoConfiguration:主配置类,负责检测环境并加载合适的容器配置
  • ServletWebServerFactory:工厂接口,负责创建具体的 Web 服务器实例
  • WebServer:抽象接口,代表实际运行的 Web 服务器(如 Tomcat、Jetty)
  • ServerProperties:配置属性类,绑定 application.properties 中的 server.* 配置
  • EmbeddedWebServerFactoryCustomizerAutoConfiguration

条件注解的作用

  • @ConditionalOnClass:确保类路径中存在指定类时才生效
  • @ConditionalOnWebApplication:确保应用是 Web 应用时才生效
  • @ConditionalOnMissingBean:确保容器中不存在指定 Bean 时才创建

工作流程

其中 Spring Boot 自动配置嵌入式 Servlet 容器的核心类是 ServletWebServerFactoryAutoConfiguration,从其源码我们能得知

ServletWebServerFactoryAutoConfiguration 分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 是一个配置类,定义Spring Bean。
@Configuration(
// 该配置类中的@Bean方法不会被CGLIB代理调用,而是直接返回新的实例。适用于配置类内部没有Bean依赖的情况
proxyBeanMethods = false
)
// 指定该自动配置类的加载顺序
@AutoConfigureOrder(Integer.MIN_VALUE)
// 仅在类路径中存在ServletRequest类时生效,即项目中引入了Servlet相关的依赖
@ConditionalOnClass({ServletRequest.class})
// 仅在当前应用是Servlet Web应用(而非Reactive Web应用)时生效。
@ConditionalOnWebApplication(
type = Type.SERVLET
)
// 启用ServerProperties配置绑定
@EnableConfigurationProperties({ServerProperties.class})
// 导入其他配置类或组件和其他嵌入式容器的自动配置类,Spring Boot会根据类路径中的依赖动态选择其中一个
@Import({BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {}

这个配置类的主要功能:

  • 通过 @Import 导入三种容器的配置类,但只会根据类路径中存在的依赖激活其中一个
  • @EnableConfigurationProperties 绑定 ServerProperties,使 application.properties 中的配置生效,这些属性会在自动配置阶段被注入到嵌入式容器的工厂类(如TomcatServletWebServerFactory)中。
  • @AutoConfigureOrder 确保该配置类优先加载

源码分析:

  • ServletWebServerFactoryAutoConfiguration ⾃动配置了嵌⼊式容器场景,ServerProperties 类通过 @ConfigurationProperties 注解绑定配置文件中的属性,这些属性会被注入到工厂类中,影响服务器的行为

  • 绑定了ServerProperties 配置类,所有和(嵌入式容器)服务器有关的配置都在以 server开头的配置

    image-20250611104002322
    • tomcat 调优就在 server-tomcat所有配置
  • ServletWebServerFactoryAutoConfiguration 导入了嵌入式的三大服务器 TomcatJettyUndertow

    • 导⼊ TomcatJettyUndertow 都有条件注解。系统中有这个类才行(也就是导了包)

    • 默认 Tomcat 配置⽣效。给容器中放 TomcatServletWebServerFactory

    • 都给容器中 ServletWebServerFactory 放了⼀个 web服务器工厂(造web服务器的)

    • web服务器工厂 都有⼀个功能,getWebServer 获取web服务器

      1
      2
      3
      4
      5
      6
      7
      public WebServer getWebServer(ServletContextInitializer... initializers) {
      if (this.disableMBeanRegistry) {
      Registry.disableRegistry();
      }

      Tomcat tomcat = new Tomcat();
      File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
    • TomcatServletWebServerFactory 创建了 tomcat。

  • ServletWebServerFactory 什么时候会创建 webServer 出来,ServletWebServerApplicationContext,ioc容器,启动的时候会调⽤创建 web 服务器

  • Spring 容器刷新的时候,会预留一个时机,刷新子容器,onRefresh(),而所谓容器刷新,就是容器启动

  • refresh() 容器刷新十二大步的刷新⼦容器会调用,onRefresh(),而在 onRefresh() 方法中,ServletWebServerApplicationContext 会调用 createWebServer() 方法

1
2
3
4
5
6
7
8
9
10
11
12
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();

if (webServer == null && servletContext == null) {
// 获取容器中的工厂类
ServletWebServerFactory factory = this.getWebServerFactory();
// 创建Web服务器实例
this.webServer = factory.getWebServer(this.getSelfInitializer());
}
// ...
}

这个过程中:

  • Spring 容器从自身获取 ServletWebServerFactory Bean(例如 TomcatServletWebServerFactory
  • 调用工厂的 getWebServer() 方法创建实际的 Web 服务器
  • 服务器启动并绑定到配置的端口(默认 8080)

其中的 refresh() 容器刷新十二大步如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);

try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh(); // 第10步:刷新子容器
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
BeansException ex = var10;
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
}

this.destroyBeans();
this.cancelRefresh(ex);
throw ex;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}

}
}

  • 总结出来就是一句话,Web 场景的 Spring 容器启动,在 onRefresh 的时候,会调用创建 Web 服务器的方法

  • 而 Web 服务器的创建是通过 WebServerFactory 搞定的。容器中又会根据导了什么包条件注解,启动相关的服务器配置,默认EmbeddedTomcat会给容器中放⼀个TomcatServletWebServerFactory ,导致项目启动,⾃动创建出Tomcat。

sequenceDiagram
    participant App as 应用启动
    participant AutoConfig as 自动配置
    participant Container as Spring容器
    participant Factory as 服务器工厂
    participant Server as Web服务器
    
    App->>AutoConfig: 加载ServletWebServerFactoryAutoConfiguration
    AutoConfig->>AutoConfig: 检查条件注解
    AutoConfig->>AutoConfig: 导入对应容器配置
    AutoConfig->>Container: 注册ServletWebServerFactory
    
    App->>Container: 调用refresh()
    Container->>Container: 执行onRefresh()
    Container->>Factory: 获取WebServerFactory
    Factory->>Server: 创建WebServer实例
    Server->>Server: 启动并监听端口
    Server-->>Container: 返回WebServer
    Container-->>App: 启动完成

自定义嵌入式 Servlet 容器

为什么 Tomcat 是默认的容器

这就涉及到 Spring Boot 的默认绑定机制

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 自动包含spring-boot-starter-tomcat传递依赖

  • 依赖关系可通过mvn dependency:tree查看:

    1
    2
    [INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.7.0
    [INFO] | \- org.springframework.boot:spring-boot-starter-tomcat:jar:2.7.0

而Reactive Web 应用

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  • 其中自动包含reactor-netty核心依赖
img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--引入springboot父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>

<dependencies>
<!--引入启动器依赖 里面就有默认的tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

如何切换嵌入式 Servlet 容器

Spring Boot 支持四种嵌入式容器实现,分别对应不同的技术场景:

容器类型 技术类型 默认绑定启动器 特点
Tomcat Servlet Web spring-boot-starter-web 默认实现,稳定性高
Jetty Servlet Web spring-boot-starter-jetty 高性能,适合长连接场景
Undertow Servlet Web spring-boot-starter-undertow 低内存消耗,非阻塞IO模型
Netty Reactive Web spring-boot-starter-webflux 异步非阻塞,高并发处理

以上 Web 容器均被 Spring Boot 嵌入至其中作为其核心特性,来简化 Spring Boot应用启动流程。Spring Boot 通过 Maven 依赖来切换应用的嵌入式容器类型,其对应的 Maven jar 分别是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>

<!-- undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

<!-- jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

<!-- netty Web Server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</dependency>

前三者是 Servlet Web 实现,最后则是 Reactive Web 的实现。值得注意的是,当我们引用的是 Servlet Web 功能模块时,它会自动集成 Tomcat ,里面包含了 TomcatMaven 依赖包,也是印证了上面说的 Tomcat 是默认的 Servlet Web 容器。Servlet Web 模块的 Maven 依赖就是 web 启动场景:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

而如果引用的是 Reactive Web 功能模块时,则会默认集成 netty Web ServerReactive Web 模块的依赖如下:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

不过,上面的三种 Servlet Web 容器也能作为 Reactive Web 容器 ,并允许替换默认实现 Netty Web Server,因为 Servlet 3.1+容器同样满足 Reactive 异步非阻塞特性。

我们需要切换不同的容器,就需要在启动器依赖里把 Tomcat 给排除,然后换成自己需要的容器类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 关键步骤:排除Tomcat -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入Jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

如果需要切换到 Netty 也是一样

因为web里引入的是tomcat容器,所以这里首先排除tomcat容器,然后引入Netty容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!--引入springboot父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>

<dependencies>
<!--引入启动器依赖 里面就有默认的tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除tomcat-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--引入Netty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>

而其中装配什么就生效什么,也是依赖于上述的条件装配机制,每个容器配置类都有@ConditionalOnClass注解,并且通过spring-autoconfigure-metadata.json定义加载顺序

graph TD
  A[启动应用] --> B[检测类路径]
  B --> C{存在Tomcat类?}
  C -->|是| D[加载Tomcat工厂]
  C -->|否| E{存在Jetty类?}
  E -->|是| F[加载Jetty工厂]
  E -->|否| G[加载Undertow工厂]

我们可以通过使用如下代码确认当前容器

1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args)
.getBean(ServletWebServerFactory.class)
.getClass()
.getName(); // 输出工厂类全限定名
}
}

但是更常用的从启动日志分析就可以看出

1
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)

关键日志标识:

  • Tomcat: o.s.b.w.embedded.tomcat
  • Jetty: o.s.b.w.embedded.jetty
  • Undertow: o.s.b.w.embedded.undertow

如果同时存在多个容器依赖

  • Spring Boot会按以下优先级选择:

    1. Tomcat
    2. Jetty
    3. Undertow
  • 也可以通过@Order注解控制:

    1
    2
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    public class MyCustomContainerAutoConfiguration {}

不同Spring Boot版本对容器的支持存在差异:

Spring Boot Tomcat Jetty Undertow Netty
2.7.x 9.0 9.4 2.2 1.0
3.0.x 10.0 11.0 2.3 1.1

建议通过spring-boot-dependencies管理版本,避免冲突。

三种机制

Spring Boot 提供了灵活的机制来定制嵌入式 Tomcat 容器,开发者可通过配置文件、工厂类定制或完全自定义配置三种方式实现。

容器选择机制

Spring Boot 根据类路径中的依赖自动选择容器:

  • 默认使用 Tomcat(spring-boot-starter-web 包含 Tomcat 依赖)
  • 若排除 Tomcat 并添加 Jetty/Undertow 依赖,则自动切换

工厂类的作用:

当容器选择完成后,对应的工厂类会被注册到 Spring 容器中:

  • Tomcat:TomcatServletWebServerFactory
  • Jetty:JettyServletWebServerFactory
  • Undertow:UndertowServletWebServerFactory

这些工厂类实现了 ServletWebServerFactory 接口,负责创建和配置具体的 Web 服务器实例。

以Tomcat为例子,展示如何配置

通过配置文件定制 Tomcat 参数

application.propertiesapplication.yml 中通过 server.tomcat 前缀配置 Tomcat 特性,以下是常用配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 基础服务器配置
server.port=8080 # 服务端口
server.servlet.context-path=/app # 上下文路径

# Tomcat 连接池配置
server.tomcat.max-threads=200 # 最大工作线程数(默认200)
server.tomcat.min-spare-threads=10 # 最小空闲线程数(默认10)
server.tomcat.max-connections=10000 # 最大连接数(默认10000)
server.tomcat.uri-encoding=UTF-8 # URL编码格式

# 超时配置
server.tomcat.connection-timeout=20000 # 连接超时时间(毫秒,默认20000)
server.tomcat.accesslog.enabled=true # 启用访问日志
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %Dms # 日志格式

# SSL 配置(HTTPS)
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat

通过工厂类定制 Tomcat 组件

通过自定义 TomcatServletWebServerFactory Bean 实现更深度的定制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.valves.RemoteIpValve;

@Configuration
public class TomcatCustomizationConfig {

// 方式1:直接创建TomcatServletWebServerFactory Bean
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

// 配置端口
factory.setPort(8081);

// 配置上下文路径
factory.setContextPath("/custom");

// 自定义连接器(添加HTTP/2支持)
factory.addAdditionalTomcatConnectors(createHttp2Connector());

// 添加Valve(如远程IP转换)
factory.addContextValves(new RemoteIpValve());

// 配置JSP支持(需添加tomcat-embed-jsp依赖)
factory.configureContext((context) -> {
context.addLifecycleListener((event) -> {
if (event.getType().equals("start")) {
// 上下文启动时的自定义逻辑
}
});
});

return factory;
}

// 方式2:通过WebServerFactoryCustomizer接口定制
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
return factory -> {
// 配置最大线程数
factory.setMaxThreads(250);
// 配置URI编码
factory.setUriEncoding("UTF-8");
// 配置访问日志
factory.getTomcatConnectorCustomizers().add(connector -> {
connector.setProperty("maxKeepAliveRequests", "1000");
});
};
}

// 辅助方法:创建HTTP/2连接器
private Connector createHttp2Connector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11Nio2Protocol");
connector.setPort(8443);
connector.setSecure(true);
connector.setScheme("https");

// 配置SSL
connector.setProperty("sslProtocol", "TLS");
connector.setProperty("keystoreFile", "classpath:keystore.p12");
connector.setProperty("keystorePass", "password");
connector.setProperty("keystoreType", "PKCS12");

// 启用HTTP/2
connector.setProperty("maxHttpHeaderSize", "8192");
connector.setProperty("http2", "true");
return connector;
}
}