Spring Boot 使用外置 Tomcat 部署详解:从配置到运行全流程
Spring Boot 默认集成了嵌入式 Tomcat,可通过 java -jar 直接启动应用,但在某些场景下(如企业统一管理服务器、需要特定 Tomcat 配置),需将应用部署到外置 Tomcat 中。从 “打包方式修改→依赖调整→启动类改造→部署运行” 四个维度,详细讲解 Spring Boot 应用部署到外置 Tomcat 的完整流程,并解答常见问题,帮你顺利完成外置容器部署。
外置 Tomcat 部署的适用场景
在决定使用外置 Tomcat 前,需明确其适用场景,避免不必要的复杂配置:
- 企业级服务器管理:大型企业通常有统一的服务器集群(如 Tomcat 集群),需将应用部署到指定外置容器;
- 自定义 Tomcat 配置:需要修改 Tomcat 核心配置(如线程池、连接器、安全配置),且这些配置无法通过 Spring Boot 嵌入式容器参数覆盖;
- 多应用共享容器:多个 Spring Boot 应用共享同一个 Tomcat 实例,节省服务器资源;
- 兼容性需求:依赖特定版本的 Tomcat(如因安全漏洞需使用定制化 Tomcat 版本)。
若无需上述场景,优先使用 Spring Boot 嵌入式 Tomcat(开发效率高、部署简单)。
部署到外置 Tomcat 的核心步骤
步骤 1:修改打包方式为 WAR
Spring Boot 默认打包为 JAR(包含嵌入式容器),部署到外置 Tomcat 需改为 WAR 包(不包含嵌入式容器,仅包含应用代码)。
在 pom.xml 中修改打包类型:
1 | <!-- 关键:将打包方式从 jar 改为 war --> |
步骤 2:排除嵌入式 Tomcat 依赖
为避免与外置 Tomcat 产生冲突,需排除 Spring Boot 自带的嵌入式 Tomcat 依赖,并添加 Servlet API 依赖(外置 Tomcat 会提供该 API)。
完整依赖配置:
1 | <!-- Web 依赖:排除嵌入式 Tomcat --> |
版本兼容性说明:
| 外置 Tomcat 版本 | 兼容的 Servlet API 版本 | 建议 Spring Boot 版本 |
|---|---|---|
| Tomcat 7.x | Servlet 3.0 | Spring Boot 1.x |
| Tomcat 8.5.x | Servlet 3.1 | Spring Boot 2.x |
| Tomcat 9.x | Servlet 4.0 | Spring Boot 2.x/3.x |
| Tomcat 10.x | Jakarta Servlet 5.0 | Spring Boot 3.x |
注意:Spring Boot 3.x 开始使用 Jakarta EE 规范(包名从
javax.servlet改为jakarta.servlet),若使用 Tomcat 10+ 搭配 Spring Boot 3.x,需将 Servlet API 依赖改为jakarta.servlet:jakarta.servlet-api:5.0.0。
步骤 3:改造启动类(继承 SpringBootServletInitializer)
嵌入式 Tomcat 通过 main 方法启动应用,而外置 Tomcat 需通过 Servlet 容器初始化机制 启动,因此需改造启动类,继承 SpringBootServletInitializer 并重写 configure 方法(替代传统 web.xml 的作用)。
启动类改造示例:
1 | import org.springframework.boot.SpringApplication; |
原理说明:
SpringBootServletInitializer是 Spring Boot 提供的适配类,用于将 Spring 应用上下文集成到 Servlet 容器(如 Tomcat);- 外置 Tomcat 启动时,会调用
configure方法,通过SpringApplicationBuilder构建并初始化 Spring 上下文,替代传统web.xml中配置的ContextLoaderListener; - 保留
main方法不影响外置部署,且便于本地开发时使用嵌入式 Tomcat 快速启动(无需切换配置)。
步骤 4:打包并部署到外置 Tomcat
1. 打包 WAR 包
通过 Maven 命令打包(确保项目无编译错误):
1 | # 清理并打包(跳过测试以加快速度) |
打包成功后,在项目的 target 目录下会生成 WAR 包(如 my-application-0.0.1-SNAPSHOT.war)。
2. 部署到外置 Tomcat
- 将生成的 WAR 包复制到外置 Tomcat 的
webapps目录下; - 启动 Tomcat(执行
bin/startup.sh或bin\startup.bat); - Tomcat 会自动解压 WAR 包,生成与 WAR 包同名的目录(如
my-application-0.0.1-SNAPSHOT)。
3. 访问应用
应用访问路径为:http://localhost:8080/[WAR包名]/[接口路径],例如:
- WAR 包名为
my-application-0.0.1-SNAPSHOT.war; - 接口路径为
/hello; - 完整访问地址:
http://localhost:8080/my-application-0.0.1-SNAPSHOT/hello。
4. 自定义访问路径(可选)
若需简化访问路径(如去掉版本号),有两种方式:
- 重命名 WAR 包:将 WAR 包重命名为
ROOT.war,访问路径变为http://localhost:8080/[接口路径](Tomcat 默认根路径); - 配置 Context 路径:在 Tomcat 的
conf/server.xml中添加<Context>配置(参考前文 “Tomcat 热部署” 部分),指定访问路径。
常见问题与解决方案
1. 部署后启动报错:ClassNotFoundException: javax.servlet.ServletContext
问题原因:
- 未添加
javax.servlet-api依赖,或依赖scope未设为provided,导致编译时缺少 Servlet API; - Servlet API 版本与外置 Tomcat 不兼容(如 Tomcat 8.5 使用 Servlet 3.0 依赖)。
解决方案:
- 确保添加正确的 Servlet API 依赖(版本与 Tomcat 匹配),且
scope为provided; - 检查
pom.xml中是否有冲突的 Servlet API 依赖(如其他第三方依赖引入了低版本 Servlet API)。
2. 应用启动成功,但访问接口报 404
问题原因:
- 访问路径错误:未包含 WAR 包名(如 WAR 包为
demo.war,需访问http://localhost:8080/demo/hello); - 上下文路径配置冲突:Spring Boot 配置了
server.servlet.context-path,与 WAR 包名叠加导致路径错误; - 启动类未继承 SpringBootServletInitializer:外置 Tomcat 无法加载 Spring 上下文,导致接口未注册。
解决方案:
- 确认访问路径包含 WAR 包名(或已配置为根路径);
- 移除
application.properties中的server.servlet.context-path配置(外置 Tomcat 以 WAR 包名作为上下文路径); - 检查启动类是否正确继承
SpringBootServletInitializer并实现configure方法。
3. 依赖冲突:org.springframework.web.context.ContextLoaderListener 重复
问题原因:
- 项目中手动添加了
spring-web等依赖,与 Spring Boot Starter 依赖冲突,导致 Tomcat 启动时加载多个ContextLoaderListener。
解决方案:
优先使用 Spring Boot Starter 依赖(如
spring-boot-starter-web),避免手动添加零散的 Spring 依赖;通过
mvn dependency:tree命令查看依赖树,排除冲突的依赖:1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>xxx</groupId>
<artifactId>xxx</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</exclusion>
</exclusions>
</dependency>
4. 外置 Tomcat 启动慢,日志提示 “DataSource 初始化超时”
问题原因:
- 外置 Tomcat 启动时,Spring 初始化数据源(如数据库连接池)耗时过长,超过 Tomcat 的默认超时时间。
解决方案:
- 优化数据源配置(如减少初始化连接数、调整连接超时参数);
- 延长 Tomcat 的启动超时时间:修改
conf/server.xml的<Connector>标签,添加connectionTimeout="60000"(60 秒); - 配置 Spring 延迟初始化数据源:在数据源 Bean 上添加
@Lazy注解,延迟到首次使用时初始化。
部署与开发的最佳实践
1. 区分开发与生产环境
- 开发环境:使用嵌入式 Tomcat(通过
main方法启动),配合热部署工具(如spring-boot-devtools),提高开发效率; - 生产环境:部署到外置 Tomcat,通过
application-prod.properties配置生产环境参数(如数据库地址、日志级别),避免硬编码。
多环境配置示例:
1 | # application-dev.properties(开发环境,嵌入式 Tomcat) |
启动时通过 --spring.profiles.active=prod 指定环境(外置 Tomcat 可通过 set SPRING_PROFILES_ACTIVE=prod 设置环境变量)。
2. 外置 Tomcat 性能优化
部署到生产环境时,需优化外置 Tomcat 配置以提升性能:
调整线程池:修改
conf/server.xml的<Executor>标签,配置核心线程数、最大线程数:1
2<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="200" minSpareThreads="20" maxIdleTime="60000"/>启用压缩:开启 HTTP 压缩,减少网络传输量:
1
2
3<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1"
compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/css,application/json"/>禁用 AJP 连接器:若不使用 AJP 协议(如与 Apache 服务器集成),注释掉
conf/server.xml中的 AJP 连接器,减少资源占用。
总结
Spring Boot 应用部署到外置 Tomcat 的核心是 “适配 Servlet 容器的初始化机制”,关键步骤可概括为:
- 修改打包方式为 WAR;
- 排除嵌入式 Tomcat 依赖,添加 Servlet API 依赖;
- 启动类继承
SpringBootServletInitializer,重写configure方法; - 打包 WAR 包并部署到外置 Tomcat 的
webapps目录
v1.3.10