升 Boot4 后,Unix-like 上给 uber jar 嵌 embedded launch scripts、做成 fully executable(chmod +x ./app.jar)的能力已移除。本稿讲怎么认、怎么改用 java -jar(或按需 Gradle application plugin)、怎么验,不写 CLASSIC loader 或其它 Features Removed。
同系列:Hazelcast 会话怎么迁、Kafka retry 属性怎么改、Pulsar Reactive 怎么改、ES 换 Rest5Client、Jackson3 怎么安全迁——一句对照,不写进本稿交付。
Boot4 为什么不能再打 fully executable jar?
Spring Boot 4.0 Migration Guide「Features Removed」里,Embedded Executable Uber Jar Launch Scripts 写得很直:
Support for embedded launch scripts, used to create “fully executable” jar files, has been removed. The support was specific to Unix-like operating systems and had a number of limitations, most notably conflicting with the recommendations for efficient deployments. If you still require similar functionality, we recommend looking at alternatives such as Gradle’s application plugin.
You can still create uber jars with Spring Boot’s build plugins and run them using java -jar.
也就是说:不是「Boot4 不能再打 uber jar」,而是去掉了 Unix-like 上把启动脚本嵌进 jar、做成 chmod +x ./app.jar 直接跑的那套能力。官方点名局限多、且与高效部署建议冲突。
常见现象对照:
| 升 4.0 后 ./app.jar / chmod +x 跑不通 | embedded launch scripts 已移除 |
| 打包任务仍开 launchScript / fully executable,构建告警或失败 | Boot4 不再支持该能力 |
| systemd / 部署脚本仍按「可执行文件」调 jar | 启动方式依赖已删脚本封装 |
| 误以为「Boot4 不能打 fat jar 了」 | 混淆:uber jar 仍可打,只是启动改 java -jar |
本稿只盯 可执行脚本 / fully executable 失效怎么认、怎么改、怎么验,其它 Boot4 主题见文首内链。
先过一张迁移总览图:

图:Boot3 fully executable / embedded launch script → Boot4 移除 → 默认 java -jar;仍需类似包装则看 Gradle application plugin。
embedded launch scripts 去掉后怎么启动?
核心动作就三步:
启动命令(示意)
# Boot 3 常见(勿再依赖 —— Boot 4 已移除 embedded launch scripts)
# chmod +x app.jar
# ./app.jar
# Boot 4 推荐
java -jar app.jar
容器 / systemd / 编排入口同理:把 ExecStart=/opt/app/app.jar 一类「当可执行文件」的写法,改成显式 java -jar(或你们统一的 JVM 启动包装,但那是运维侧脚本,不是 Boot 再嵌脚本进 jar)。
Maven 打包仍可打 uber jar(示意)
<!– spring-boot-maven-plugin 继续打可执行 uber jar –>
<!– 关键的是「再嵌一层 Unix launch script / fully executable」这类配置,不是整插件 –>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!– 勿再依赖 launchScript / fully executable 相关开关 –>
</plugin>
Gradle 打包(示意)
plugins {
id("org.springframework.boot")
}
// bootJar 仍产出 uber jar;启动用 java -jar
// 若仍需「类似包装」,按 Migration Guide 评估 application plugin
父 POM overlay、CI 打包参数、Dockerfile ENTRYPOINT、Helm values 里硬编码的 ./app.jar / chmod +x,都要扫一遍——Boot 不再嵌脚本,并不等于远端部署脚本会自动改掉。
还能用 java -jar 吗?
能,而且这是官方明文推荐的主路径。 Guide 第二段写得很清楚:You can still create uber jars with Spring Boot’s build plugins and run them using java -jar。
实务上抓住三点就够:
半吊子最差形态:构建侧仍开 launch script 开关(告警/失败)+ 部署侧还 chmod +x ./app.jar + 排障时却去改 loaderImplementation=CLASSIC——把同页另一条 Removed 当成了本稿主线。
和 CLASSIC loader 移除是一回事吗?
不是。 同页另有 Classic Uber-Jar Loader Support 移除(涉及 loaderImplementation / CLASSIC),那是 loader 实现 的坑,不是 launch scripts。
| Migration Guide 条目 | Embedded Executable Uber Jar Launch Scripts | Classic Uber-Jar Loader Support |
| 典型症状 | ./app.jar / fully executable 失效 | loaderImplementation=CLASSIC 相关失败 |
| 主改法 | java -jar;可选 Gradle application plugin | 去掉 CLASSIC loader 配置(另文/另 MR) |
| 能否混判 | 禁——一句对照即可,勿写成同一坑 | 勿抢本稿主线 |
升版清单里若写「把 fully executable 和 CLASSIC 一起当『可执行 jar 全挂了』修」,容易两边都改不干净。建议:launch scripts 单独 MR;CLASSIC 若撞上再开对照,不要揉进同一提交叙事。
要用 Gradle application plugin 吗?
不是人人都要。 Guide 的条件句是:If you still require similar functionality,再看 Gradle application plugin 等替代。
选型建议(作者推断,非官方强制):
- 部署本来就是 java -jar / 容器 java -jar → 什么额外包装都不必加,只停用 launch script 相关配置即可;
- 本地/运维习惯「一个可执行文件拉起」且构建链是 Gradle → 评估 application plugin 产出启动脚本/发行布局;
- Maven 为主、又不需要类 Unix 可执行封装 → 继续 Boot 插件打 uber jar + java -jar 即可;
- 不要在 MR 里夹带未证实的「Boot 官方新嵌脚本开关」——证据包禁止编造。
怎么验证迁移已生效?
验收不要只看「应用能启动」。下面几条建议当成门禁:

图:检查打包是否仍启用 launch script → 改 java -jar → 对照 Migration Guide;并标注勿与 CLASSIC loader 混判。
1. 全文搜旧 fully executable / launch script 依赖
全仓库(含 CI、Dockerfile、systemd unit、Helm values、父 POM overlay)搜:
launchScript
fullyExecutable
fully-executable
chmod +x
./app.jar
EmbeddedExecutable
路径正确时,生产部署入口不应再把 jar 当「自带 shell 头的可执行文件」;若搜到,应只剩迁移说明 / 历史注释,而不是仍被加载的 profile。
2. 确认 uber jar 仍能打、且用 java -jar 能起
# 示意:构建后用显式 java -jar 验收(版本/路径以你们工程为准)
java -jar build/libs/app.jar
# 或
java -jar target/app.jar
验收口令建议写成:
验收口令:
仓库/CI/编排搜不到仍生效的 fully executable / launchScript 依赖
启动命令已改为 java -jar(或等价 JVM 显式启动)
Boot 构建插件仍能打出 uber jar
未把 CLASSIC loader 当成 launch scripts 的「修复开关」
对照 Migration Guide「Embedded Executable Uber Jar Launch Scripts」段
3. 启动栈与 Migration Guide 交叉核对
改完后重新打包、启动:不应再因 launch script / fully executable 相关任务失败或文档提示「已移除仍在用」。若仍失败,优先怀疑还有一层父 POM / 共享约定 / 旧 Dockerfile 在开旧能力。
需要包装时:按 Gradle application plugin 文档验收发行布局(本稿不编造具体 task 名与未在 Guide 出现的包装器)。
4. 业务侧:进程起来、健康检查仍按设计工作
门禁上至少确认:进程监听端口、readiness、滚动发布入口与升版前产品预期一致——用来排除「只是本地改了、预发还在 ./app.jar」的半吊子状态。
关于「起不来 / 打包红」的排障顺序,实务上更稳妥的是:
- 先看是否还在开 launch script / fully executable;
- 再看部署是否仍 chmod +x ./app.jar;
- 再看是否误把 CLASSIC loader 当同一坑在改;
- 再看 uber jar 是否其实已打出、只是启动命令错了;
- 最后才怀疑业务代码、端口占用或基础镜像里的 java 本身。
前几步都是 Boot4 Migration Guide 文档级变化;把顺序弄反,容易在「loader / 分层 jar」侧耗一整天,最后发现只是启动脚本没改完。
推荐迁移顺序(同一 MR)
按下面四步收口,比「先改本地 Dockerfile 再慢慢补 CI」少返工:
配置中心 / 多环境若各有一份启动命令,每一层都要清掉 ./app.jar 可执行依赖,避免「本地改了、预发还挂着」。灰度顺序建议:先单实例验收,再滚动其余节点。和 Hazelcast Session 移除、Kafka retry 改名一样,launch scripts 移除适合单独开一个小 MR:只动打包开关 + 启动命令 + 验收,不要和 Session / Kafka / Pulsar / Undertow 揉在同一提交里。
边界与不要踩的坑
- 不要写「Boot4 不能再打 uber jar」——官方明文:仍可打,只是去掉 embedded launch scripts。
- 不要把 CLASSIC loader(loaderImplementation=CLASSIC)写成本稿主线;可一句对照「别混」。
- 不要编造「恢复 fully executable 的官方开关」或其它未在 Guide 点名的包装器名。
- 不要把 Gradle application plugin 写成「人人必装」——仅 仍需类似功能 时评估。
- 不要与 Hazelcast Session、Kafka retry、Pulsar Reactive、Undertow、Session Mongo 混成主线——各有专文。
- 不要编造搜索量、公司事故、未证实 API;证据包没有的属性名 / 插件坐标不写。
- 不要推荐已 BLOCKED 的 WebClient timeout 写法。
- 真相源:Migration Guide · Embedded Executable Uber Jar Launch Scripts。
和「半吊子升级」怎么划清
实务里最常见的失败不是「命令记错」,而是 只做了一半:
- 本地 bootJar 已不嵌脚本,CI 父 POM 仍开 fully executable;
- 或者:构建绿了,systemd / K8s 入口还在 ./app.jar;
- 再或者:启动改了 java -jar,却跑去改 loaderImplementation=CLASSIC,两边叙事搅在一起;
- 再或者:以为「Boot4 不能打 fat jar」,整段换成奇怪的自定义打包,反而偏离 Guide。
这几类半吊子状态,日志往往比「业务挂了」更像「权限不足 / 不是可执行文件 / 未知打包选项」。门禁上建议把「搜 launchScript / fullyExecutable / ./app.jar」和「确认 java -jar 已落地」写成同级检查:全仓库 + 编排导出一起搜。门禁不过就别合主分支。
升级窗口里,先单实例验通再滚动,比「全量一起切、出问题再回滚」便宜得多。若你们同时还在动其它 Boot4 Features Removed 项,记得:本稿只保证 embedded launch scripts / fully executable 怎么认、怎么改、怎么验 写清;Session / Kafka / Pulsar 等请另开对照,避免混判。
FAQ
Boot4 为什么不能再打 fully executable jar? Embedded launch scripts 已移除(Unix-like;局限多、与高效部署建议冲突)。不是不能打 uber jar。
embedded launch scripts 去掉后怎么启动? 继续用 Boot 构建插件打 uber jar,启动改为 java -jar app.jar;编排层同步改掉 chmod +x ./app.jar。
还能用 java -jar 吗? 能。这是 Migration Guide 明文推荐的主路径。
和 CLASSIC loader 移除是一回事吗? 不是。CLASSIC 是同页另一条 Uber-Jar Loader 移除;本稿主线是 launch scripts。勿混 MR。
要用 Gradle application plugin 吗? 仅当仍需类似包装时,按 Guide 评估;不是默认必选项。Maven 场景多数直接 java -jar 即可。
这和 Hazelcast Session / Kafka retry 有什么关系? 同属 Boot4 升级踩坑系列,但主题不同:Hazelcast 是 Session 直持移除,Kafka 是 retry 键改名,本稿是可执行 jar 启动脚本移除。分开 MR,避免混判。
小结 checklist
- 认清 Removed:embedded launch scripts / fully executable(Unix-like)
- 仍可用 Boot 构建插件打 uber jar + java -jar
- 停用 launchScript / fully executable 相关构建与部署依赖
- 编排 / systemd / Dockerfile 入口改为显式 java -jar
- 仍需包装 → 评估 Gradle application plugin(勿编造其它官方名)
- 勿与 CLASSIC loader 混成同一坑(一句对照即可)
- 勿写「Boot4 不能再打 uber jar」
- 验收:搜旧 fully executable 依赖 + java -jar 起服 + 对照 Guide
- 勿与 Hazelcast Session / Kafka retry / Pulsar / Undertow / Session Mongo 混主线
- 勿编造未证实 API / 恢复开关;证据外名称不写
- 真相源:Spring Boot 4.0 Migration Guide
按「停用 launch script → 改 java -jar →(可选)Gradle application plugin → Guide 验收」改完,这类踩坑通常一次就能闭环。别在 loader / 分层 jar 侧把 CLASSIC 当成同一坑硬猜。其它 Boot4 能力见前述内链,本文不展开。



