定制化tomcat
Spring Boot 定制化内置 Tomcat 详解:1.x 与 2.x+ 版本适配指南
Spring Boot 内置的 Tomcat 容器虽提供默认配置,但实际开发中常需定制化调整(如修改端口、编码、线程池、 session 超时等)。不同 Spring Boot 版本(1.x 与 2.x+)提供的定制化接口存在差异,核心是从 EmbeddedServletContainerCustomizer(1.x)演进为 WebServerFactoryCustomizer(2.x+)。从 “1.x 版本定制→2.x+ 版本定制→核心配置场景→实战示例” 四个维度,系统讲解内置 Tomcat 的定制方法,帮你跨版本适配容器配置需求。
核心背景:版本演进与接口差异
Spring Boot 2.x 对内置服务器(Tomcat/Jetty/Undertow)的抽象层进行了重构,核心变化是将 “嵌入式 Servlet 容器” 的概念升级为 “Web 服务器”,对应的定制化接口也随之调整:
| Spring Boot 版本 | 核心定制化接口 | 对应的 Tomcat 工厂类 | 适用场景 |
|---|---|---|---|
| 1.x 版本 | EmbeddedServletContainerCustomizer |
TomcatEmbeddedServletContainerFactory |
1.x 版本定制内置 Tomcat |
| 2.x+ 版本 | WebServerFactoryCustomizer |
TomcatServletWebServerFactory |
2.x+ 版本定制内置 Tomcat |
核心逻辑不变:均通过 “定制器接口” 获取 Tomcat 工厂类,再通过工厂类设置容器参数(如端口、编码、线程池)。
1.x 版本:基于 EmbeddedServletContainerCustomizer 定制
Spring Boot 1.x 中,通过实现 EmbeddedServletContainerCustomizer 接口,可获取 TomcatEmbeddedServletContainerFactory 工厂类,进而定制 Tomcat 配置。
1. 核心步骤
(1)实现 EmbeddedServletContainerCustomizer 接口
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.stereotype.Component;
// 1. 标注 @Component,让 Spring 扫描并加载该定制器
public class Tomcat1xCustomizer implements EmbeddedServletContainerCustomizer {
public void customize(org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer container) {
// 2. 强转为 Tomcat 专属工厂类(区分 Jetty/Undertow)
TomcatEmbeddedServletContainerFactory tomcatFactory = (TomcatEmbeddedServletContainerFactory) container;
// 3. 定制 Tomcat 配置(示例:session 超时、编码、线程池)
// 3.1 设置 session 超时时间(5 分钟,单位:毫秒)
tomcatFactory.setSessionTimeout(300000);
// 3.2 设置 URI 编码(解决中文乱码)
tomcatFactory.setUriEncoding(java.nio.charset.StandardCharsets.UTF_8);
// 3.3 定制 Tomcat 连接器(如端口、协议)
tomcatFactory.addConnectorCustomizers(connector -> {
// 设置端口(优先级低于配置文件 server.port)
connector.setPort(8081);
// 设置协议(默认 HTTP/1.1,可改为 org.apache.coyote.http11.Http11NioProtocol 启用 NIO)
connector.setProtocol("org.apache.coyote.http11.Http11NioProtocol");
// 设置最大连接数
connector.setMaxConnections(1000);
});
// 3.4 定制 Tomcat 引擎(如默认主机)
tomcatFactory.addEngineValves(valve -> {
// 示例:添加访问日志 Valve(记录请求日志)
if (valve instanceof org.apache.catalina.valves.AccessLogValve) {
org.apache.catalina.valves.AccessLogValve accessLogValve = (org.apache.catalina.valves.AccessLogValve) valve;
accessLogValve.setPattern("%h %l %u %t \"%r\" %s %b"); // 日志格式
accessLogValve.setDirectory("logs"); // 日志目录
accessLogValve.setPrefix("tomcat-access-"); // 日志前缀
}
});
}
}
(2)生效方式
- 只需将定制器类标注
@Component(或通过@Bean注册),Spring Boot 会自动发现并执行customize方法; - 配置优先级:代码定制的参数 低于 配置文件(如
server.port),若配置文件已设置,代码定制会被覆盖。
2. 1.x 版本关键 API 说明
| 工厂类方法 | 作用描述 | 示例值 |
|---|---|---|
setSessionTimeout(int) |
设置 session 超时时间(单位:毫秒) | 300000(5 分钟) |
setUriEncoding(Charset) |
设置 URI 编码(解决中文路径乱码) | StandardCharsets.UTF_8 |
addConnectorCustomizers() |
定制 Tomcat 连接器(端口、协议、连接数) | connector -> connector.setMaxConnections(1000) |
addEngineValves() |
定制 Tomcat 引擎(访问日志、安全 Valve) | 添加 AccessLogValve 记录请求日志 |
2.x+ 版本:基于 WebServerFactoryCustomizer 定制
Spring Boot 2.x+ 废弃了 EmbeddedServletContainerCustomizer,改用 WebServerFactoryCustomizer 接口,对应的 Tomcat 工厂类也从 TomcatEmbeddedServletContainerFactory 改为 TomcatServletWebServerFactory,但定制逻辑与 1.x 基本一致。
1. 核心步骤
(1)实现 WebServerFactoryCustomizer 接口
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
// 1. 标注 @Component,注册为 Spring Bean
public class Tomcat2xCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
public void customize(ConfigurableServletWebServerFactory factory) {
// 2. 强转为 Tomcat 专属工厂类(2.x+ 对应 TomcatServletWebServerFactory)
TomcatServletWebServerFactory tomcatFactory = (TomcatServletWebServerFactory) factory;
// 3. 定制 Tomcat 配置(示例:编码、线程池、SSL、访问日志)
// 3.1 设置 URI 编码(解决中文乱码)
tomcatFactory.setUriEncoding(StandardCharsets.UTF_8);
// 3.2 定制 Tomcat 线程池(核心线程、最大线程、空闲时间)
tomcatFactory.addConnectorCustomizers(connector -> {
// 获取 Tomcat 连接器的线程池配置
org.apache.tomcat.util.threads.ThreadPoolExecutor executor =
(org.apache.tomcat.util.threads.ThreadPoolExecutor) connector.getProtocolHandler().getExecutor();
if (executor != null) {
executor.setCorePoolSize(10); // 核心线程数(默认 10)
executor.setMaximumPoolSize(100); // 最大线程数(默认 200)
executor.setKeepAliveTime(60, java.util.concurrent.TimeUnit.SECONDS); // 空闲线程存活时间
}
});
// 3.3 定制访问日志(2.x+ 简化了日志配置)
tomcatFactory.setAccessLogEnabled(true); // 开启访问日志
tomcatFactory.setAccessLogPattern("%h %l %u %t \"%r\" %s %b %D"); // 日志格式(%D 为请求耗时,单位:微秒)
tomcatFactory.setAccessLogDirectory(new java.io.File("logs")); // 日志目录
// 3.4 定制 SSL(HTTPS 配置,需提前准备证书)
tomcatFactory.addAdditionalTomcatConnectors(createSslConnector());
}
// 辅助方法:创建 SSL 连接器(配置 HTTPS)
private org.apache.catalina.connector.Connector createSslConnector() {
org.apache.catalina.connector.Connector connector = new org.apache.catalina.connector.Connector("org.apache.coyote.http11.Http11NioProtocol");
org.apache.tomcat.util.net.SSLHostConfig sslHostConfig = new org.apache.tomcat.util.net.SSLHostConfig();
org.apache.tomcat.util.net.SSLHostConfig.Certificate certificate = new org.apache.tomcat.util.net.SSLHostConfig.Certificate();
// 配置证书路径(绝对路径或 classpath 路径)
certificate.setCertificateKeystoreFile("classpath:ssl/localhost-ssl.jks");
certificate.setType("RSA");
certificate.setCertificateKeystorePassword("123456"); // 证书密码
sslHostConfig.addCertificate(certificate);
connector.addSslHostConfig(sslHostConfig);
connector.setPort(8443); // HTTPS 端口
return connector;
}
}
(2)生效方式
与 1.x 一致:标注
@Component后,Spring Boot 会自动调用customize方法;若需更精准的类型匹配,可指定泛型为TomcatServletWebServerFactory(避免强转):
// 直接指定泛型为 Tomcat 工厂类,无需强转 public class Tomcat2xCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> { public void customize(TomcatServletWebServerFactory tomcatFactory) { // 直接操作 tomcatFactory,无需强转 tomcatFactory.setUriEncoding(StandardCharsets.UTF_8); } }
2. 2.x+ 版本关键 API 新增特性
相比 1.x,2.x+ 的 TomcatServletWebServerFactory 新增了更便捷的 API,简化常见配置:
| 新增 API | 作用描述 | 示例 |
|---|---|---|
setAccessLogEnabled(boolean) |
快速开启 / 关闭访问日志 | tomcatFactory.setAccessLogEnabled(true) |
setAccessLogPattern(String) |
设置访问日志格式 | "%h %l %u %t \"%r\" %s %b" |
addAdditionalTomcatConnectors() |
新增额外连接器(如同时支持 HTTP 和 HTTPS) | 新增 8443 端口的 SSL 连接器 |
setContextPath(String) |
设置应用上下文路径(替代 1.x 的 setContextPath) |
tomcatFactory.setContextPath("/demo") |
核心定制场景实战(2.x+ 版本为例)
以下是内置 Tomcat 最常见的定制场景,基于 2.x+ 版本实现,覆盖编码、线程池、HTTPS、session 超时等需求。
场景 1:解决中文乱码(URI + 响应编码)
public class TomcatEncodingCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
public void customize(TomcatServletWebServerFactory factory) {
// 1. 设置 URI 编码(解决 URL 中中文参数乱码)
factory.setUriEncoding(StandardCharsets.UTF_8);
// 2. 设置响应编码(解决响应体中文乱码)
factory.addConnectorCustomizers(connector -> {
org.apache.coyote.http11.Http11NioProtocol protocol = (org.apache.coyote.http11.Http11NioProtocol) connector.getProtocolHandler();
protocol.setURIEncoding("UTF-8");
protocol.setUseBodyEncodingForURI(true); // 响应体编码与 URI 一致
});
}
}
场景 2:优化线程池(提升并发能力)
Tomcat 线程池参数直接影响应用的并发处理能力,需根据服务器 CPU 核心数合理配置:
public class TomcatThreadPoolCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> {
// 获取 Tomcat 内置线程池
org.apache.tomcat.util.threads.ThreadPoolExecutor executor =
(org.apache.tomcat.util.threads.ThreadPoolExecutor) connector.getProtocolHandler().getExecutor();
if (executor == null) {
// 若未创建线程池,手动创建(通常 Spring Boot 会自动创建)
executor = new org.apache.tomcat.util.threads.ThreadPoolExecutor(
10, // 核心线程数(CPU 核心数 + 1)
100, // 最大线程数(核心线程数的 5-10 倍,避免上下文切换开销)
60, // 空闲线程存活时间(秒)
java.util.concurrent.TimeUnit.SECONDS,
new java.util.concurrent.LinkedBlockingQueue<>(1000) // 任务队列容量
);
executor.setThreadNamePrefix("tomcat-thread-"); // 线程名称前缀(便于日志排查)
connector.getProtocolHandler().setExecutor(executor);
} else {
// 调整已有线程池参数
executor.setCorePoolSize(10);
executor.setMaximumPoolSize(100);
executor.setKeepAliveTime(60, java.util.concurrent.TimeUnit.SECONDS);
}
});
}
}
场景 3:配置 HTTPS(支持 SSL 证书)
需提前准备 SSL 证书(如通过 keytool 生成 JKS 格式证书),放置在 src/main/resources/ssl 目录:
public class TomcatSslCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
public void customize(TomcatServletWebServerFactory factory) {
// 1. 配置 HTTPS 连接器(8443 端口)
org.apache.catalina.connector.Connector sslConnector = new org.apache.catalina.connector.Connector("org.apache.coyote.http11.Http11NioProtocol");
org.apache.tomcat.util.net.SSLHostConfig sslHostConfig = new org.apache.tomcat.util.net.SSLHostConfig();
org.apache.tomcat.util.net.SSLHostConfig.Certificate cert = new org.apache.tomcat.util.net.SSLHostConfig.Certificate();
// 2. 证书配置(classpath 路径)
cert.setCertificateKeystoreFile("classpath:ssl/localhost-ssl.jks");
cert.setCertificateKeystorePassword("123456"); // 证书密码(生成时设置)
cert.setType("RSA"); // 证书类型
sslHostConfig.addCertificate(cert);
sslConnector.addSslHostConfig(sslHostConfig);
sslConnector.setPort(8443); // HTTPS 端口
// 3. 添加 HTTPS 连接器(同时支持 HTTP 8080 和 HTTPS 8443)
factory.addAdditionalTomcatConnectors(sslConnector);
// 4. 可选:强制 HTTP 重定向到 HTTPS
factory.addEngineValves(valve -> {
if (valve instanceof org.apache.catalina.valves.rewrite.RewriteValve) {
org.apache.catalina.valves.rewrite.RewriteValve rewriteValve = (org.apache.catalina.valves.rewrite.RewriteValve) valve;
rewriteValve.setConfiguration("RewriteCond %{SERVER_PORT} ^8080$ RewriteRule ^(.*)$ https://%{SERVER_NAME}:8443$1 [R,L]");
}
});
}
}
场景 4:设置 session 超时时间
public class TomcatSessionCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
public void customize(TomcatServletWebServerFactory factory) {
// 设置 session 超时时间为 30 分钟(单位:分钟,2.x+ 支持直接传分钟数)
factory.setSessionTimeout(Duration.ofMinutes(30));
// 1.x 版本需传毫秒数:factory.setSessionTimeout(1800000);
}
}
版本迁移注意事项(1.x → 2.x+)
若需从 Spring Boot 1.x 迁移到 2.x+,定制化 Tomcat 时需注意以下兼容性调整:
- 接口替换:
EmbeddedServletContainerCustomizer→WebServerFactoryCustomizer; - 工厂类替换:
TomcatEmbeddedServletContainerFactory→TomcatServletWebServerFactory; - 依赖调整:确保
spring-boot-starter-web依赖版本为 2.x+,避免混合 1.x 依赖; - API 调整:
- session 超时:1.x 的
setSessionTimeout(int 毫秒)→ 2.x+ 的setSessionTimeout(Duration); - 访问日志:2.x+ 新增
setAccessLogEnabled/setAccessLogPattern,替代 1.x 的addEngineValves手动配置;
- session 超时:1.x 的
- 泛型支持:2.x+ 建议指定
WebServerFactoryCustomizer<TomcatServletWebServerFactory>泛型,避免强转。
总结
Spring Boot 定制内置 Tomcat 的核心逻辑跨版本一致 ——“通过定制器接口获取工厂类,再通过工厂类调整容器参数”,主要差异在于接口和工厂类的名称:
- 1.x 版本:
EmbeddedServletContainerCustomizer+TomcatEmbeddedServletContainerFactory; - 2.x+ 版本:
WebServerFactoryCustomizer+TomcatServletWebServerFactory(推荐,API 更便捷)