Spring Boot 默认打包成 fat jar 导致体积庞大(80–150MB),因其将所有依赖(如 Tomcat、Spring)全打入单个 jar;改用 THIN layout 或分离 lib 目录可降至 10–20MB,提升 Docker/K8s 部署效率。

为什么 spring-boot-maven-plugin 默认打包会很大
因为默认的 repackage 目标会把所有依赖(包括 Tomcat、Spring、Logback 等)全塞进一个 fat jar 里,哪怕你只用了一个 @RestController,也得带上整套 Spring Boot 启动器和嵌入式容器。实际业务代码可能就几 MB,但最终 jar 动辄 80–150MB。
常见错误现象:java -jar app.jar 启动慢、Docker 镜像层臃肿、CI/CD 上传耗时、K8s 拉取镜像失败报 ImagePullBackOff。
- 不是所有依赖都需要打进 jar:比如
spring-boot-starter-tomcat是 provided scope,但默认仍被重打包进去 -
spring-boot-maven-plugin的layout=ZIP(默认)是 fat jar 根本原因 - 本地开发用 fat jar 方便,但生产部署真没必要——尤其做容器化或灰度发布时
用 thin layout 替换默认打包方式
Spring Boot 官方支持 thin layout,它只打包你自己的 class 和 resources,依赖全部外置,启动时按需加载(类似传统 WAR 的 classpath 逻辑)。
关键不是“删依赖”,而是“不打包依赖”——依赖由运行时环境提供,或通过 lib 目录挂载。
- 在
pom.xml中把spring-boot-maven-plugin的layout改成THIN:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>THIN</layout>
</configuration>
</plugin>
- 执行
mvn package后得到的app.jar通常只有 10–20MB,不含任何第三方 jar - 它依赖
thin.properties文件控制依赖解析路径,默认从 Maven 本地仓库加载,也可配成 HTTP 或 classpath - 注意:
THINlayout 不兼容java -jar直接运行,必须用java -cp app.jar org.springframework.boot.loader.thin.ThinJarLauncher
分离依赖到 lib 目录并复用
更可控的做法是把依赖抽成独立 lib/ 目录,和应用 jar 分开管理。这样升级依赖不用重打整个 jar,CI/CD 也能利用缓存跳过未变的依赖层。
使用场景:Docker 多阶段构建、K8s ConfigMap 挂载共享 lib、灰度时只替换主 jar。
- 加插件
dependency:copy-dependencies到package生命周期:
<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>
<excludeScope>provided</excludeScope>
</configuration>
</execution>
</executions>
</plugin>
- 打包后结构是:
target/app.jar+target/lib/*.jar - 启动命令变成:
java -cp "app.jar:lib/*" com.example.Application(Linux/macOS)或java -cp "app.jar;lib/*" ...(Windows) - 容易踩的坑:
lib/路径必须与启动命令中-cp的写法严格匹配;spring-boot-maven-plugin要禁用 repackage,否则会干扰 classpath
瘦身后的启动类和配置要注意什么
瘦 jar 不再自带 org.springframework.boot.loader.JarLauncher,所以不能靠 MANIFEST.MF 的 Start-Class 自动启动,必须显式指定主类,且确保所有依赖在 classpath 上。
性能影响很小,但兼容性上要注意:某些依赖(如 spring-boot-devtools)会检测是否运行在 fat jar 环境,瘦 jar 下可能失效;@ConditionalOnClass 逻辑不受影响,因为 classpath 仍是完整的。
- MANIFEST.MF 中不要设
Spring-Boot-Classes或Spring-Boot-Lib—— 这些是 fat jar 专用字段 - 如果用了自定义
ClassLoader或反射加载资源,确认路径逻辑不依赖jar:file:/...协议(瘦 jar 是普通 jar,资源走file:/) - Dockerfile 中推荐用多阶段构建:第一阶段解压依赖到
/app/lib,第二阶段只 COPYapp.jar和lib/,避免把 .m2 缓存带进镜像
真正麻烦的不是怎么瘦,而是团队里有人顺手加了个 compile scope 的新 SDK,忘了改 scope,结果下次打包又胖回 100MB——这种事发生过不止一次。










