Spring Boot 项目打包部署详解

Spring Boot 项目打包部署详解

Spring Boot 项目最终要运行起来,必须经过打包和部署两个环节。打包方式决定了部署方式,选错了会导致应用无法启动。

一、两种打包方式:Jar 与 War

Spring Boot 支持两种打包方式:Jar 和 War。

打包方式 运行方式 Web 容器来源 适用场景
Jar java -jar app.jar 内嵌 Tomcat/Jetty/Undertow 微服务、云原生、前后端分离
War 部署到外置 Tomcat 的 webapps 目录 外置 Tomcat 或其他容器 传统企业应用、必须使用外置容器的场景

Spring Boot 官方推荐 Jar 包方式。内嵌容器带来的好处是:本地开发和线上运行环境完全一致,不存在"本地跑得起来,部署到服务器就挂了"的问题。

如何切换打包方式?

pom.xml 中设置 <packaging> 标签:

xml 复制代码
<!-- Jar 包(默认) -->
<packaging>jar</packaging>

<!-- War 包 -->
<packaging>war</packaging>

二、Jar 包部署(默认方式)

这是 Spring Boot 最推荐的部署方式。应用和容器打包在一起,直接通过 java -jar 运行。

第一步:配置 spring-boot-maven-plugin

spring-boot-starter-parent 已经帮你配置好了这个插件,通常不需要额外配置。但如果你想生成带有完整依赖信息的清单文件,可以显式添加:

xml 复制代码
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>com.example.MyApplication</mainClass>
        <layout>JAR</layout>
    </configuration>
</plugin>

spring-boot-maven-plugin 的核心功能有两个:一是 repackage 目标,把普通 Jar 重打成可执行的 Fat Jar;二是 run 目标,直接在开发阶段快速启动应用。通过 mvn packagemvn spring-boot:repackage 触发重打包。

第二步:执行打包命令

bash 复制代码
mvn clean package

执行后,target/ 目录下会生成两个 Jar 文件:

  • myapp.jar:可执行的 Fat Jar,包含所有依赖
  • myapp.jar.original:Maven 生成的原始 Jar,不包含依赖

第三步:运行

bash 复制代码
java -jar target/myapp.jar

常用运行参数:

bash 复制代码
# 指定端口
java -jar myapp.jar --server.port=8081

# 指定配置文件
java -jar myapp.jar --spring.config.location=/path/to/application.yml

# 指定环境
java -jar myapp.jar --spring.profiles.active=prod

# 设置 JVM 参数
java -Xmx512m -Xms256m -jar myapp.jar

后台运行:

Linux / Mac:

bash 复制代码
nohup java -jar myapp.jar > logs/out.log 2>&1 &

Windows(PowerShell):

powershell 复制代码
Start-Process -NoNewWindow java -ArgumentList "-jar myapp.jar"

或者注册为 Windows 服务。

三、War 包部署(传统方式)

某些企业环境要求必须使用外置 Tomcat,这时候需要打 War 包。

第一步:修改打包方式

xml 复制代码
<packaging>war</packaging>

第二步:排除内嵌 Tomcat

xml 复制代码
<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>

<!-- 如果使用 Servlet API,需要显式添加 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>

第三步:修改启动类

java 复制代码
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(MyApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

SpringBootServletInitializer 是 Spring Boot 提供的适配类,让 War 包能在外置容器中启动。它重写了 Spring 的 WebApplicationInitializer 接口,容器启动时会调用 onStartup 方法,再由 Spring Boot 初始化 ApplicationContext。如果不继承这个类,外置容器不会加载 Spring Boot 的配置。

第四步:打包

bash 复制代码
mvn clean package

生成 target/myapp.war

第五步:部署

将 War 包复制到 Tomcat 的 webapps 目录,Tomcat 会自动解压并部署。

bash 复制代码
cp target/myapp.war /opt/tomcat/webapps/
cd /opt/tomcat/bin
./startup.sh

访问路径:http://localhost:8080/myapp/xxx

四、多环境配置

不同环境需要不同的配置:开发用本地数据库,测试用测试库,生产用生产库。

Spring Boot 支持通过 Profile 区分环境:

方式一:多个配置文件

复制代码
application.yml           # 公共配置
application-dev.yml       # 开发环境
application-test.yml      # 测试环境
application-prod.yml      # 生产环境

方式二:单个 yml 多文档块

yaml 复制代码
spring:
  profiles:
    active: dev
---
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8080
---
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 80

启动时指定环境:

bash 复制代码
java -jar myapp.jar --spring.profiles.active=prod

五、配置文件外部化

生产环境通常不希望把数据库密码等敏感信息写在打包好的 Jar 包里,而是通过外部配置文件覆盖。

Spring Boot 的配置优先级从高到低:

  1. 命令行参数
  2. 操作系统环境变量
  3. Jar 包外的 application.ymlconfig/ 目录下优先级更高)
  4. Jar 包内的 application.yml

指定外部配置文件:

bash 复制代码
java -jar myapp.jar --spring.config.location=/opt/config/application-prod.yml

通过环境变量设置:

bash 复制代码
export SPRING_PROFILES_ACTIVE=prod
java -jar myapp.jar

六、打包原理:Fat Jar 的结构

Spring Boot 的可执行 Jar 之所以能通过 java -jar 直接运行,是因为它改变了 Jar 包的结构。普通的 Jar 包只包含当前项目的 class 文件和资源文件,不包含依赖包,Java 的默认类加载器无法从嵌套的 Jar 中加载类。

Spring Boot 的 spring-boot-maven-pluginrepackage 阶段,把 Maven 解析出来的所有依赖 Jar 包解压后重新打包,同时生成一个 META-INF/MANIFEST.MF 文件,其中的 Main-Class 指向 JarLauncherStart-Class 指向应用的 main 方法所在的类。

JarLauncher 是 Spring Boot 自定义的类加载入口,它读取 BOOT-INF/lib/ 目录下的所有依赖 Jar,通过自定义的 LaunchedURLClassLoader 加载这些类,然后通过反射调用 Start-Class 中的 main 方法。这个设计让应用能够以单个文件的形式分发,同时保持了类加载的完整性。

War 包的结构与 Jar 包类似,区别在于它遵循 Java EE 的 War 规范,可以被外置容器识别,启动入口也改为了 SpringBootServletInitializer

七、常见问题与解决方案

问题 1:打包后运行报 no main manifest attribute

原因:没有使用 spring-boot-maven-plugin 重新打包,或者该插件未配置。

解决:在 pom.xml 中添加 spring-boot-maven-plugin 并执行 mvn spring-boot:repackage。需要保证 mainClass 配置正确。

问题 2:Jar 包太大

原因:所有依赖都打包进去了。

解决:

  • 使用 spring-boot-maven-pluginexcludes 排除不需要的依赖
  • 使用 spring-boot-thin-launcherlayers 分层打包

问题 3:线上启动时端口被占用

解决:在启动命令中指定 --server.port=8081,或者通过环境变量 SERVER_PORT=8081 设置。

问题 4:配置文件不生效

检查配置文件的加载顺序和覆盖关系。命令行参数 > 外部配置文件 > 内部配置文件。

问题 5:War 包部署后返回 404

原因:启动类没有继承 SpringBootServletInitializer,或者应用上下文路径不正确。

解决:继承 SpringBootServletInitializer,并重写 configure 方法。部署后访问路径需要加上上下文路径,默认是 War 包文件名。

八、总结

部署方式 命令 适用场景
Jar 包开发环境 java -jar myapp.jar 日常开发测试
Jar 包后台运行 nohup java -jar myapp.jar & 生产环境(微服务)
Jar 包指定环境 java -jar myapp.jar --spring.profiles.active=prod 环境隔离
War 包部署 拷贝到 Tomcat webapps 目录 传统企业应用、必须使用外置容器的场景
相关推荐
pnoker36 分钟前
拆解 IoT DC3:六层微服务架构
java·物联网·spring cloud·微服务·架构
cfm_291442 分钟前
ThreadLocal 内存泄漏
java·jvm
我的xiaodoujiao44 分钟前
Django 基础知识详细图文教程 4-Django 视图定义与使用
开发语言·数据库·后端·测试工具·django·sqlite
NJCloud1 小时前
Docker 镜像管理:分层结构、构建优化与最佳实践
java·docker·容器
vipxieliang1 小时前
收货地址验证完整方案:省市区与详细地址
java·spring boot
Apifox1 小时前
Apifox 8 月更新|调试、权限与协作体验持续优化
前端·后端·测试
kyson_1 小时前
后端登录凭证设计:Session-Cookie、Redis-Token 与 JWT
后端
对象存储与RustFS1 小时前
RustFS 后台扫描器与自愈调参:五档速度、位腐检测周期与并发上限
后端·rust·开源
Json____1 小时前
java-宿舍安全卫生检查系统项目源码
java·前端·javascript·课程设计·it学习·wwwoop.com