导读:本期聚焦于IT柏拉图创作的《Spring Boot Jar 包体积太大怎么办?几种实用的瘦身部署方案详解》,敬请观看详情。Spring Boot 项目打包出来的可执行 Jar 动辄一两百兆,其中真正的业务代码往往只占几兆,绝大部分体积被依赖的第三方库占据。每次发版都要上传一个巨大的文件,不仅浪费带宽,还拖慢了 CI/CD 流水线的部署速度。本文围绕 Jar 包为什么这么大的原因展开分析,对比了几种主流的瘦身思路,包括 layer 分层打包配合 Docker 缓存、把依赖库外置到 lib 目录、使用 spring-boot-thin-launcher 按需下载依赖,以及借助 ProGuard 混淆压缩等方案。每种方案都给出了具体的配置代码和适用场景,并分析了优缺点,最后总结了不同规模项目应该如何选择合适的组合策略,帮助你在保证部署可靠性的前提下把传输体积压缩到最小。

把一个简单的 Spring Boot 项目打成可执行 Jar,体积轻松超过 50MB,如果再引入一些数据源驱动、缓存客户端、消息中间件的 SDK,冲到 150MB 以上也很常见。而真正属于你自己写的业务代码,通常只有几兆。剩下的体积全是依赖库、内嵌 Tomcat、资源文件这些东西。体积大带来的问题不只是占磁盘,更麻烦的是每次发布都要传输这个大文件,内网上传慢、容器镜像推送拉取慢、多机部署时同步慢,这些都会拖累整个交付流程。这篇文章就来系统梳理一下 Jar 包为什么大,以及几种经过实践验证的瘦身方案。

Spring Boot Jar 包体积太大怎么办?几种实用的瘦身部署方案详解

先搞清楚 Jar 包为什么这么大

瘦身之前得先知道体积都被什么吃掉了。Spring Boot 的可执行 Jar 采用嵌套式结构,BOOT-INF/lib 目录下存放了所有依赖的第三方库,BOOT-INF/classes 下是业务代码,org/springframework/boot/loader 下是引导加载器。绝大多数情况下,体积的大头都在 BOOT-INF/lib 里。

可以直接用压缩软件打开 Jar 查看,也可以用命令解压分析。下面这个命令可以列出 Jar 内所有文件并按大小排序:

# 解压后统计 lib 目录下依赖的大小,按体积倒序
unzip -o app.jar -d app-output
du -sh app-output/BOOT-INF/lib/* | sort -rh | head -20

常见的大户包括:各种日志实现(logback、log4j2 相关 jar)、内嵌 Tomcat、数据库驱动(MySQL、Oracle 的驱动都不小)、云厂商 SDK(OSS、SLS 这类 SDK 经常带一堆传递依赖)、以及一些被全量引入但只用了一小部分功能的组件,比如引入了 spring-cloud-starter 全家桶却只用了其中一两个功能。分析清楚构成之后,第一步应该做的是清理无用依赖:检查 pom.xml 中那些为了图方便引入的宽泛依赖,换成精确的子模块依赖;对于 scope 为 compile 但只在编译期需要的库,改成 provided。这一步往往不花多少力气就能减掉几十兆。

方案一:分层打包配合 Docker 镜像缓存

Spring Boot 2.3 之后官方支持分层打包(Layered Jar),它不减少 Jar 本身的体积,但能大幅减少每次实际传输的数据量。原理是把 Jar 内容按照变化频率拆成多个层:依赖库基本不变放一层,业务代码频繁变化单独一层。构建 Docker 镜像时,各层分别作为镜像层,只要依赖没变,这些层在镜像仓库中就直接复用缓存,推送和拉取时只传输变化的业务代码那一层。

开启分层打包只需要两步。第一步在 pom.xml 中配置:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers>
            <enabled>true</enabled>
        </layers>
    </configuration>
</plugin>

第二步在 Dockerfile 中使用 unpack 模式提取各层:

FROM eclipse-temurin:17-jre as builder
WORKDIR application
COPY target/app.jar app.jar
# 按层解压,提取顺序即依赖层到应用层
RUN java -Djarmode=layertools -jar app.jar extract

FROM eclipse-temurin:17-jre
WORKDIR application
# 依赖层几乎不变,充分利用镜像缓存
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

这个方案的优点是官方原生支持、无侵入、效果稳定,配合镜像仓库的缓存机制,日常发版真正传输的数据量可能只有几兆。缺点是它只适用于容器化部署,如果你还是传统的虚拟机部署方式,分层打包帮不上忙。另外要注意自定义分层顺序,如果项目里有经常变化的本地依赖,应该把它放到 application 层附近,避免破坏缓存。

方案二:依赖外置,业务 Jar 单独更新

传统服务器部署场景下更实用的做法是把依赖库从业务 Jar 中剥离出来。打包时只把第三方依赖放到一个独立的 lib 目录,业务代码单独打成一个轻量 Jar,启动时通过 classpath 指定依赖路径。这样每次发版只需要更新几百 KB 到几 MB 的业务 Jar,依赖目录除非有变化否则长期不动。

在 Maven 中配置依赖拷贝和精简打包:

<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>
                <includeScope>runtime</includeScope>
            </configuration>
        </execution>
    </executions>
</plugin>

启动命令改为指定 classpath:

java -Dloader.path=lib -cp app.jar org.springframework.boot.loader.PropertiesLauncher

这里用了 PropertiesLauncher 作为启动器,它支持通过 loader.path 加载外部目录中的 Jar。注意使用这种方式时,Jar 仍需用 Spring Boot 插件打包,但业务代码和依赖分离后,上传体积骤减。这个方案适合服务器直部署、发布频率高的项目。缺点是需要维护两份产物的版本一致性,如果依赖目录和业务 Jar 版本不匹配,可能出现运行时找不到类的诡异问题,建议在部署脚本中加入依赖目录的版本校验,或者依赖变更时全量发布一次。

方案三:thin launcher 与启动时按需拉取依赖

还有一种更激进的思路:业务 Jar 里完全不包含依赖,首次启动时根据 pom 信息自动从 Maven 仓库下载依赖到本地缓存。这就是 spring-boot-thin-launcher 插件的工作方式。打包产物通常只有几 MB,甚至几百 KB。

<plugin>
    <groupId>org.springframework.boot.experimental</groupId>
    <artifactId>spring-boot-thin-maven-plugin</artifactId>
    <version>1.0.30.RELEASE</version>
    <executions>
        <execution>
            <id>thin-packge</id>
            <goals>
                <goal>resolve</goal>
            <goals>
        </execution>
    </executions>
</plugin>

首次启动会触发依赖下载,之后依赖缓存在本地 ~/.m2/thin/repo 目录,后续启动速度正常。这个方案的明显短板是对网络环境有强依赖,目标服务器必须能访问 Maven 仓库(或内部私服),而且首次部署时间较长。在内网管控严格、无法直连仓库的环境里要谨慎使用。另外该插件属于 experimental 项目,更新不算活跃,生产环境采用前建议充分验证。

其他补充手段与方案选型建议

除了上面三种主流方案,还有一些辅助手段值得一提。一是压缩比优化,Spring Boot 打包时可排除不必要的资源,比如文档、源码 Jar、测试资源;二是用 spring-boot-maven-pluginrequiresUnpack 处理某些必须解压才能加载的库;三是考虑换用 GraalVM Native Image,产物体积和启动速度都有质的飞跃,但改造成本高,反射、动态代理相关的问题需要逐一适配,适合新项目或对启动速度极端敏感的场景。

选型上可以参考这样的组合:容器化部署的项目,首选 layer 分层打包,配置成本最低收益最大;传统虚拟机部署、发版频繁的项目,采用依赖外置方案,配合发布脚本管理依赖目录;边缘节点多、带宽极差但可控的场景,可以考虑 thin launcher。无论选哪种,第一步永远是把 pom 里的无用依赖清干净,这是零成本且必须做的事。瘦身不是一刀切,要结合部署架构、团队运维习惯和回滚需求综合决定,切莫为了追求小体积牺牲了部署的可追溯性和回滚的便捷性。

Spring Boot瘦身Jar包体积优化分层打包修改时间:2026-09-06 10:46:42

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260906/51496.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。