当前位置: 代码网 > it编程>编程语言>Java > 将SpringBoot应用从JAR转换为WAR包并部署到外部Tomcat全过程

将SpringBoot应用从JAR转换为WAR包并部署到外部Tomcat全过程

2026年03月03日 Java 我要评论
在现代 java web 开发中,spring boot 凭借其“约定优于配置”的理念和内嵌服务器的便利性,已经成为构建微服务和单体应用的首选框架。默认情况下,spring b

在现代 java web 开发中,spring boot 凭借其“约定优于配置”的理念和内嵌服务器的便利性,已经成为构建微服务和单体应用的首选框架。默认情况下,spring boot 项目被打包为可执行的 jar 文件,并通过内嵌的 tomcat、jetty 或 undertow 启动。然而,在某些企业环境中,尤其是那些已有传统 java ee 基础设施或强制要求使用外部 servlet 容器(如 apache tomcat)的场景下,我们需要将 spring boot 应用打包为 war(web application archive)格式,以便部署到独立的 tomcat 服务器上。

本文将深入探讨如何将一个标准的 spring boot 应用从 jar 转换为 war 包,并成功部署到外部 tomcat 容器中。我们将涵盖项目结构改造、依赖调整、启动类修改、构建配置、本地测试以及生产部署等完整流程,并辅以详细的代码示例和原理说明。无论你是刚接触 spring boot 的新手,还是需要应对企业级部署需求的开发者,本文都将为你提供清晰、实用的指导。

为什么选择 war 包部署?

尽管 spring boot 的内嵌容器带来了极大的开发便利性,但在实际生产环境中,仍有不少理由促使我们选择传统的 war 包部署方式:

企业合规与标准化
许多大型企业拥有统一的应用服务器管理策略,要求所有 web 应用必须部署在中央管理的 tomcat 或 weblogic 实例上,便于监控、日志集中、安全策略实施和资源隔离。

共享资源与性能调优
在高并发场景下,多个应用共享同一个 jvm 和 servlet 容器实例可以减少内存开销(尽管需谨慎处理类加载冲突)。同时,运维团队可能对 tomcat 有深度调优经验,更倾向于使用熟悉的外部容器。

遗留系统集成
当 spring boot 应用需要与旧版 java ee 应用共存于同一服务器时,war 部署是自然的选择,便于共享会话、上下文或数据库连接池等资源。

ci/cd 流水线兼容
某些企业的持续集成/持续部署流水线已经围绕 war 包构建了完整的自动化流程(如 jenkins + tomcat manager),迁移成本较高。

注意:spring boot 官方文档明确指出,虽然支持 war 部署,但推荐优先使用可执行 jar + 内嵌容器的方式,因为这是 spring boot 设计的核心优势所在。只有在确实需要时才选择 war 方案。

准备工作:创建一个基础 spring boot 项目

在开始改造之前,我们先创建一个最简单的 spring boot web 应用作为起点。你可以使用 spring initializr 快速生成项目骨架。

项目配置(maven)

  • project: maven project
  • language: java
  • spring boot: 3.2.x(或最新稳定版)
  • packaging: jar(初始为 jar,后续改为 war)
  • java version: 17(或你环境支持的 lts 版本)
  • dependencies: spring web, spring boot devtools(可选)

生成并导入 ide 后,你会得到如下核心文件结构:

demo-war-app/
├── pom.xml
├── src/
│   └── main/
│       ├── java/
│       │   └── com/example/demowarapp/
│       │       ├── demowarappapplication.java
│       │       └── controller/
│       │           └── hellocontroller.java
│       └── resources/
│           └── application.properties

核心代码示例

demowarappapplication.java(主启动类):

package com.example.demowarapp;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;

@springbootapplication
public class demowarappapplication {
    public static void main(string[] args) {
        springapplication.run(demowarappapplication.class, args);
    }
}

hellocontroller.java(简单 rest 接口):

package com.example.demowarapp.controller;

import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.restcontroller;

@restcontroller
public class hellocontroller {

    @getmapping("/hello")
    public string sayhello() {
        return "hello from spring boot war!";
    }
}

此时,运行 mvn spring-boot:run 或直接执行主方法,应用将在内嵌 tomcat 上启动,默认端口 8080,访问 http://localhost:8080/hello 可看到返回信息。

第一步:修改打包方式为 war

要生成 war 包,首先需要告诉构建工具(maven 或 gradle)改变默认的打包类型。

maven 配置 (pom.xml)

<packaging> 标签中指定为 war

<packaging>war</packaging>

完整片段如下:

<groupid>com.example</groupid>
<artifactid>demo-war-app</artifactid>
<version>0.0.1-snapshot</version>
<packaging>war</packaging> <!-- 关键修改 -->
<name>demo-war-app</name>
<description>demo project for spring boot war deployment</description>

gradle 配置 (build.gradle)

如果你使用 gradle,则需应用 war 插件:

plugins {
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.4'
    id 'java'
    id 'war' // 添加此行
}

group = 'com.example'
version = '0.0.1-snapshot'
sourcecompatibility = '17'

// 其他配置...

✅ 提示:maven 用户注意,spring-boot-starter-parent 已经预配置了 maven-war-plugin,通常无需额外声明。

第二步:排除内嵌 tomcat 依赖

spring boot web starter 默认包含内嵌的 tomcat 服务器(spring-boot-starter-tomcat)。当我们将应用部署到外部 tomcat 时,这个内嵌容器不仅多余,还可能引发类路径冲突(如 classnotfoundexceptionnosuchmethoderror)。

因此,必须将 spring-boot-starter-tomcat 设置为 provided scope(maven)或 providedcompile(gradle),表示该依赖在编译时需要,但在运行时由 servlet 容器提供。

maven 配置 (pom.xml)

spring-boot-starter-web 依赖中排除 tomcat,并显式声明为 provided:

<dependencies>
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-web</artifactid>
        <!-- 排除内嵌 tomcat -->
        <exclusions>
            <exclusion>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-starter-tomcat</artifactid>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- 显式添加 tomcat 依赖,scope 为 provided -->
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-tomcat</artifactid>
        <scope>provided</scope>
    </dependency>

    <!-- 其他依赖... -->
</dependencies>

gradle 配置 (build.gradle)

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web') {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
    }
    providedruntime 'org.springframework.boot:spring-boot-starter-tomcat'
    
    // 其他依赖...
}

⚠️ 重要:不要完全移除 spring-boot-starter-tomcat!spring boot 的自动配置仍需要它提供的类(如 tomcatservletwebserverfactory),只是运行时不加载内嵌服务器实例。

第三步:改造主启动类

spring boot 应用要作为 war 部署,必须实现 springbootservletinitializer 接口。该接口是 spring boot 与 servlet 3.0+ 规范的桥梁,允许应用在 servlet 容器启动时被正确初始化。

修改demowarappapplication.java

package com.example.demowarapp;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
import org.springframework.boot.builder.springapplicationbuilder;
import org.springframework.boot.web.servlet.support.springbootservletinitializer;

@springbootapplication
public class demowarappapplication extends springbootservletinitializer {

    @override
    protected springapplicationbuilder configure(springapplicationbuilder application) {
        return application.sources(demowarappapplication.class);
    }

    public static void main(string[] args) {
        springapplication.run(demowarappapplication.class, args);
    }
}

关键点解析:

  • 继承 springbootservletinitializer:这是 war 部署的必要条件。
  • 重写 configure() 方法:该方法在 servlet 容器(如 tomcat)启动 war 时被调用,用于构建 springapplication 实例。application.sources(...) 指定了主配置类。
  • 保留 main() 方法:这样应用既可以作为 war 部署,也可以通过 java -jar(如果仍打包为 jar)或 ide 直接运行,保持开发灵活性。

🔍 原理:servlet 3.0+ 规范引入了 servletcontainerinitializer 机制。spring boot 通过 springbootservletinitializer 实现了该机制,使得 war 包在部署时能自动触发 spring boot 的启动流程,而无需 web.xml

第四步:验证项目结构与构建 war 包

完成上述修改后,项目应具备正确的 war 结构。让我们检查并构建。

项目结构期望

标准 war 包应包含:

  • web-inf/classes/:编译后的 java 类和资源文件
  • web-inf/lib/:第三方依赖(不包括 provided 范围的)
  • web-inf/lib-provided/:spring boot 特有的目录,存放 provided 依赖(仅当使用 spring boot maven plugin 时)
  • meta-inf/:元数据

spring boot 的 war 包略有不同:它既是标准 war(可部署到 tomcat),又是可执行 jar(如果保留内嵌容器)。但在我们的配置下,由于排除了内嵌 tomcat,生成的 war 不可执行,只能部署到外部容器。

构建命令

maven:

mvn clean package

gradle:

./gradlew clean bootwar  # 注意:spring boot gradle plugin 使用 bootwar 任务

📌 注意:gradle 用户应使用 bootwar 而非 war 任务,因为 bootwar 会正确处理 spring boot 的特殊需求(如启动类、依赖范围等)。

构建成功后,war 文件位于:

  • maven: target/demo-war-app-0.0.1-snapshot.war
  • gradle: build/libs/demo-war-app-0.0.1-snapshot.war

你可以用解压工具打开 war 文件,确认 web-inf/lib/ 中不包含 tomcat-embed-core 等内嵌 tomcat jar。

第五步:本地 tomcat 部署与测试

现在,我们将 war 包部署到本地 tomcat 实例进行测试。

下载并安装 tomcat

  1. 访问 apache tomcat 官网 下载最新稳定版(如 10.1.x)。
  2. 解压到本地目录,例如 /opt/tomcatc:\tomcat
  3. 确保已安装 jdk 17+ 并配置好 java_home

💡 提示:tomcat 10+ 默认使用 jakarta ee 9 命名空间(jakarta.*),而 spring boot 3.x 也已迁移到 jakarta ee 9。因此务必使用 tomcat 10 或更高版本。若使用 spring boot 2.x,则需 tomcat 9(支持 javax.*)。

部署 war 包

将生成的 war 文件复制到 tomcat 的 webapps/ 目录:

cp target/demo-war-app-0.0.1-snapshot.war $tomcat_home/webapps/

启动 tomcat

进入 tomcat 的 bin/ 目录,执行启动脚本:

  • linux/macos: ./catalina.sh run
  • windows: catalina.bat run

观察控制台日志,应能看到 spring boot 应用的启动信息,类似:

info  o.s.b.w.e.tomcat.tomcatwebserver - tomcat initialized with port(s): 0 (http)
info  o.s.b.startupinfologger - starting demowarappapplication using java 17...
...
info  o.s.b.w.e.tomcat.tomcatwebserver - tomcat started on port(s): ... (http) with context path '/demo-war-app-0.0.1-snapshot'

访问应用

打开浏览器,访问:

http://localhost:8080/demo-war-app-0.0.1-snapshot/hello

你应该看到返回:

hello from spring boot war!

成功!这表明 war 包已正确部署并运行。

自定义 context path(可选)

默认情况下,war 文件名(不含 .war)即为上下文路径(context path)。你可以通过以下方式自定义:

  1. 重命名 war 文件:例如 myapp.war → 访问路径为 /myapp
  2. 配置 application.properties(仅影响内嵌容器,对外部 tomcat 无效)
  3. 使用 root.war:将 war 命名为 root.war,则上下文路径为 /(根路径)

常见问题与解决方案

在 war 部署过程中,开发者常遇到以下问题。我们逐一分析并提供解决方法。

1. 启动失败:classnotfoundexception 或 noclassdeffounderror

现象:tomcat 启动时报错,找不到 spring 或 tomcat 相关类。

原因:通常是内嵌 tomcat 未正确排除,或 provided 依赖未正确处理。

解决方案

  • 确认 pom.xmlspring-boot-starter-tomcat 的 scope 为 provided
  • 检查 war 包的 web-inf/lib/ 目录,确保无 tomcat-embed-* jar
  • 若使用 gradle,确认使用 bootwar 任务而非 war

2. 应用无法访问,返回 404

现象:tomcat 启动成功,但访问 /context-path/hello 返回 404。

原因

  • 上下文路径错误(检查 war 文件名)
  • controller 未被扫描到
  • spring boot 未正确初始化

解决方案

  • 查看 tomcat 日志,确认 spring boot 是否启动成功
  • 确保 @springbootapplication 注解的类在正确包路径下(默认扫描子包)
  • 尝试访问根路径 /context-path/,看是否返回 whitelabel error page(说明 spring mvc 已工作)

3. 静态资源(css/js)无法加载

现象:html 页面能访问,但样式和脚本 404。

原因:spring boot 默认将静态资源放在 src/main/resources/static/,war 部署时这些资源会被正确打包到 web-inf/classes/static/,应能正常访问。

解决方案

  • 确认资源文件位置正确
  • 检查浏览器开发者工具中的网络请求路径
  • 避免在 controller 中拦截所有路径(如 /**

4. 数据库连接池问题

现象:应用启动时无法连接数据库。

原因:hikaricp 等连接池在外部容器中可能因类加载器问题无法初始化。

解决方案

  • 确保数据库驱动 jar 放在 tomcat 的 lib/ 目录(全局共享),或打包进 war 的 web-inf/lib/
  • application.properties 中显式配置连接池参数

高级配置:优化 war 部署体验

除了基本部署,我们还可以进行一些优化,提升可维护性和性能。

1. 自定义启动日志

application.properties 中配置日志级别:

logging.level.org.springframework=info
logging.level.com.example=debug

日志文件默认输出到 tomcat 的 logs/catalina.out。如需独立日志文件,可配置 logback:

src/main/resources/logback-spring.xml:

<configuration>
    <appender name="file" class="ch.qos.logback.core.rolling.rollingfileappender">
        <file>logs/demo-app.log</file>
        <rollingpolicy class="ch.qos.logback.core.rolling.timebasedrollingpolicy">
            <filenamepattern>logs/demo-app.%d{yyyy-mm-dd}.log</filenamepattern>
        </rollingpolicy>
        <encoder>
            <pattern>%d{hh:mm:ss.sss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="file" />
    </root>
</configuration>

注意:确保 tomcat 进程对 logs/ 目录有写权限。

2. 外部化配置

application.properties 外部化,便于不同环境部署:

  • 将配置文件放在 tomcat 的 conf/ 目录
  • 通过 jvm 参数指定配置路径:
export catalina_opts="-dspring.config.location=file:/opt/tomcat/conf/demo-app.properties"

3. 健康检查与监控

启用 spring boot actuator:

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-actuator</artifactid>
</dependency>

application.properties 中暴露端点:

management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always

部署后访问 http://localhost:8080/context-path/actuator/health 可查看健康状态。

war 与 jar 部署对比

关键差异总结:

特性jar(内嵌容器)war(外部容器)
打包方式jarwar
服务器内嵌 tomcat/jetty外部 tomcat/weblogic
启动命令java -jar app.jar复制到 webapps/
进程模型独立 java 进程共享 tomcat 进程
配置复杂度低(开箱即用)中(需排除依赖、改启动类)
适用场景微服务、云原生、快速原型传统企业、合规要求、遗留集成

生产环境最佳实践

在将 war 包投入生产前,请遵循以下建议:

1. 使用 profile 管理环境配置

通过 spring profiles 区分开发、测试、生产环境:

application-prod.properties:

spring.datasource.url=jdbc:mysql://prod-db:3306/mydb
spring.datasource.username=prod_user
spring.jpa.hibernate.ddl-auto=validate

启动时激活 profile:

export catalina_opts="-dspring.profiles.active=prod"

2. 安全加固

  • 禁用不必要的 actuator 端点
  • 配置 https(在 tomcat 层面)
  • 使用强密码和加密配置

3. 性能调优

  • 调整 tomcat 线程池大小(server.xml 中的 maxthreads
  • 优化 jvm 参数(堆内存、gc 策略)
  • 启用 gzip 压缩(tomcat 的 compression 属性)

4. 自动化部署

结合 ci/cd 工具(如 jenkins)实现自动化:

  1. 代码提交触发构建
  2. 生成 war 包
  3. 通过 tomcat manager api 部署(需配置用户权限)

tomcat-users.xml 示例:

<tomcat-users>
    <role rolename="manager-script"/>
    <user username="deployer" password="secure_password" roles="manager-script"/>
</tomcat-users>

jenkins 部署脚本片段:

curl -u deployer:secure_password \
  http://localhost:8080/manager/text/deploy?path=/myapp \
  --upload-file target/myapp.war

替代方案:使用 undertow 或 jetty

虽然本文聚焦 tomcat,但 spring boot 也支持其他 servlet 容器。若企业使用 jetty 或 undertow,只需:

  1. 排除 spring-boot-starter-tomcat
  2. 添加对应 starter(如 spring-boot-starter-jetty
  3. 设置为 provided scope

例如 jetty(maven):

<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>
    <scope>provided</scope>
</dependency>

总结:何时选择 war 部署?

将 spring boot 应用打包为 war 并部署到外部 tomcat,是一种特定场景下的折中方案。它牺牲了 spring boot “开箱即用”的部分便利性,换取了与传统 java ee 生态的兼容性。

推荐使用 war 部署的情况

  • 企业强制要求使用中央管理的 tomcat 集群
  • 需要与旧版 war 应用共享会话或资源
  • 运维团队熟悉 tomcat 调优且不愿管理多个内嵌容器实例

应避免 war 部署的情况

  • 新建微服务项目
  • 云原生环境(kubernetes/docker)
  • 追求快速迭代和简化部署流程

无论选择哪种方式,理解其背后的原理(servlet 规范、类加载机制、依赖管理)都是关键。希望本文的详细步骤和示例能帮助你在需要时顺利实现 spring boot 的 war 部署。

以上就是将springboot应用从jar转换为war包并部署到外部tomcat全过程的详细内容,更多关于springboot应用jar转war包的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com