0%

定制化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 接口
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
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.stereotype.Component;

// 1. 标注 @Component,让 Spring 扫描并加载该定制器
@Component
public class Tomcat1xCustomizer implements EmbeddedServletContainerCustomizer {

@Override
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 接口
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
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
@Component
public class Tomcat2xCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

@Override
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(避免强转):

    1
    2
    3
    4
    5
    6
    7
    8
    // 直接指定泛型为 Tomcat 工厂类,无需强转
    public class Tomcat2xCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    @Override
    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 + 响应编码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class TomcatEncodingCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
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 核心数合理配置:

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
@Component
public class TomcatThreadPoolCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
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 目录:

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
@Component
public class TomcatSslCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
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 超时时间

1
2
3
4
5
6
7
8
9
@Component
public class TomcatSessionCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
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 时需注意以下兼容性调整:

  1. 接口替换EmbeddedServletContainerCustomizerWebServerFactoryCustomizer
  2. 工厂类替换TomcatEmbeddedServletContainerFactoryTomcatServletWebServerFactory
  3. 依赖调整:确保 spring-boot-starter-web 依赖版本为 2.x+,避免混合 1.x 依赖;
  4. API 调整:
    • session 超时:1.x 的 setSessionTimeout(int 毫秒) → 2.x+ 的 setSessionTimeout(Duration)
    • 访问日志:2.x+ 新增 setAccessLogEnabled/setAccessLogPattern,替代 1.x 的 addEngineValves 手动配置;
  5. 泛型支持:2.x+ 建议指定 WebServerFactoryCustomizer<TomcatServletWebServerFactory> 泛型,避免强转。

总结

Spring Boot 定制内置 Tomcat 的核心逻辑跨版本一致 ——“通过定制器接口获取工厂类,再通过工厂类调整容器参数”,主要差异在于接口和工厂类的名称:

  • 1.x 版本EmbeddedServletContainerCustomizer + TomcatEmbeddedServletContainerFactory
  • 2.x+ 版本WebServerFactoryCustomizer + TomcatServletWebServerFactory(推荐,API 更便捷)

欢迎关注我的其它发布渠道