Spring Boot 项目标准化部署打包实战
一、目标
将 Spring Boot 项目打包为开箱即用的生产环境部署包。用户解压后只需修改配置文件,运行启动脚本即可启动服务。
最终目录结构
your-app/
├── bin/
│ ├── startup.sh # Linux 启动脚本
│ └── startup.cmd # Windows 启动脚本
├── conf/
│ └── application.yml # 外部配置文件(解耦,方便运维修改)
├── lib/
│ ├── your-app.jar # 主程序 JAR
│ └── *.jar # 依赖 JAR
├── logs/ # 日志目录(自动创建)
└── LICENSE
设计理念
- 配置与程序分离 :
conf/application.yml独立于 JAR,运维人员无需接触代码 - 一键启动:封装好 JVM 参数和 classpath 的启动脚本,降低操作门槛
- 热迭代 :后续升级只需替换
lib/your-app.jar,配置、依赖、脚本全不动
二、环境要求
| 组件 | 版本要求 |
|---|---|
| JDK | 8+ |
| Maven | 3.6+ |
| Spring Boot | 2.x |
三、Maven 配置
3.1 pom.xml 插件配置
xml
<build>
<plugins>
<!-- 1. 依赖拷贝插件:将所有依赖 JAR 拷贝到 target/lib/ -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- 2. Spring Boot 打包插件:生成可执行 JAR -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.18.RELEASE</version>
<configuration>
<mainClass>com.your.MainApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 3. Assembly 打包插件:按自定义结构打包为 tar.gz -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/assembly/package.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
重要提示 :如果使用 JDK 8 ,
spring-boot-maven-plugin必须锁定版本为 2.1.18.RELEASE ,否则会报class file version 61.0错误(高版本插件编译的 class 与 JDK 8 不兼容)。
四、Assembly 打包配置
创建 src/assembly/package.xml:
xml
<assembly
xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>release</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>your-app</baseDirectory>
<fileSets>
<!-- 启动脚本,权限 755 -->
<fileSet>
<directory>src/assembly/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<!-- 配置文件 -->
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>conf</outputDirectory>
</fileSet>
</fileSets>
<!-- 依赖 JAR 全部打入 lib/ -->
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*:jar</include>
</includes>
</dependencySet>
</dependencySets>
</assembly>
各节点说明:
| 节点 | 作用 |
|---|---|
<id>release</id> |
最终产物名后缀:your-app-release.tar.gz |
<format>tar.gz</format> |
打包格式,跨平台通用 |
<baseDirectory> |
解压后的根目录名 |
<fileMode>0755</fileMode> |
Linux 下脚本可执行权限 |
五、启动脚本
5.1 Windows 启动脚本
src/assembly/bin/startup.cmd:
cmd
@echo off
set DIR=%~dp0..
cd %DIR%
if not exist "%DIR%\logs" mkdir "%DIR%\logs"
set JAVA_OPTS=-Xms256m -Xmx1024m -Dfile.encoding=UTF-8
java %JAVA_OPTS% ^
-Dloader.path="%DIR%\lib" ^
-Dspring.config.location=file:"%DIR%\conf\application.yml" ^
-jar "%DIR%\lib\your-app.jar" ^
> "%DIR%\logs\app.log" 2>&1
echo Started.
pause
说明 :
%~dp0是批处理脚本自身所在目录,%~dp0..回到项目根目录。^是 Windows 命令行续行符。
5.2 Linux 启动脚本
src/assembly/bin/startup.sh:
bash
#!/bin/bash
# 获取脚本所在目录的上级目录(即项目根目录)
DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$DIR"
# 创建日志目录
mkdir -p logs
# JVM 参数
JAVA_OPTS="-Xms256m -Xmx1024m -Dfile.encoding=UTF-8"
# 后台启动,日志输出到 logs/app.log
nohup java $JAVA_OPTS \
-Dloader.path="$DIR/lib" \
-Dspring.config.location=file:"$DIR/conf/application.yml" \
-jar "$DIR/lib/your-app.jar" \
> "$DIR/logs/app.log" 2>&1 &
echo "Started. PID: $!"
脚本解读:
| 配置项 | 说明 |
|---|---|
-Xms256m -Xmx1024m |
初始堆 256M,最大堆 1G(按实际调整) |
-Dloader.path |
指定依赖 JAR 加载路径,配合 PropertiesLauncher 使用 |
-Dspring.config.location |
指定外部配置文件路径,优先级高于 JAR 内的配置 |
nohup ... & |
后台运行,终端关闭不中断 |
$! |
最后启动的后台进程 PID |
六、打包与部署
6.1 打包
bash
mvn clean package
执行后在 target/ 目录下生成 your-app-release.tar.gz。
6.2 部署
bash
# 1. 解压
tar -xzf your-app-release.tar.gz
# 2. 修改配置
vim your-app/conf/application.yml
# 3. 启动
cd your-app/bin
# Linux
sh startup.sh
# Windows
startup.cmd
部署只需三步:解压 → 改配置 → 启动。
七、迭代更新
后续升级只需替换 一个 JAR 文件,流程如下:
- 本地改完代码,执行
mvn clean package - 将新的
your-app.jar上传到服务器 - 替换
lib/下的旧 JAR - 重启服务
配置、依赖、启动脚本全部不用动,真正实现热迭代。
八、常见问题
8.1 端口被占用
bash
# Windows:查看端口占用
netstat -ano | findstr :8080
# Windows:杀掉进程
taskkill /pid 进程ID /f
bash
# Linux:查看端口占用
lsof -i :8080
# Linux:杀掉进程
kill -9 进程ID
8.2 找不到主类
检查 pom.xml 中 spring-boot-maven-plugin 的 <mainClass> 配置是否正确,需指向带 @SpringBootApplication 注解的主类。
8.3 JAR 中没有主清单属性
确保 spring-boot-maven-plugin 配置了 <goal>repackage</goal>,只有 repackage 才会生成 MANIFEST.MF 中的 Main-Class。
8.4 class file version 61.0 错误
原因:高版本 Spring Boot Maven Plugin(3.x)编译的 class 需要 JDK 17+,与 JDK 8 不兼容。
解决 :JDK 8 环境下将 spring-boot-maven-plugin 版本降级到 2.1.18.RELEASE。
8.5 配置文件找不到
启动参数中的路径建议使用绝对路径 (%DIR% / $DIR),避免因工作目录不同导致相对路径解析失败。脚本中已通过 DIR 变量实现了此逻辑。
8.6 日志文件不输出
- 确认
logs/目录有写入权限 - 检查启动用户是否有当前目录的写权限
nohup输出会先写入nohup.out,确认重定向符号>正确
九、总结
通过 Maven Assembly 插件 + 外部配置文件 + 启动脚本 的组合,可以将 Spring Boot 项目打包为标准的生产环境部署包。
| 维度 | 收益 |
|---|---|
| 部署成本 | 解压 → 改配置 → 启动,三步完成 |
| 运维友好 | 配置外置,无需懂 Java 也能改配置、看日志 |
| 迭代效率 | 升级只换一个 JAR,秒级完成 |
| 跨平台 | Windows / Linux 均有对应启动脚本 |
| 标准化 | 统一的目录结构,不同项目一致,降低认知成本 |
延伸思考 :在生产环境中,可以进一步加入
-XX:+HeapDumpOnOutOfMemoryError、GC 日志、JMX 监控等 JVM 参数,结合 Systemd(Linux)或 NSSM(Windows)实现服务托管和自动重启。