Spring Boot 部署优化:打包体积缩小 80% 的秘诀

Spring Boot 部署优化:打包体积缩小 80% 的秘诀
在这里插入图片描述

✨道路是曲折的,前途是光明的!

📝 专注C/C++、Linux编程与人工智能领域,分享学习笔记!

🌟 感谢各位小伙伴的长期陪伴与支持,欢迎文末添加好友一起交流!

在这里插入图片描述
在微服务架构盛行的今天,Spring Boot 应用的打包体积直接影响着部署效率和资源成本。本文将分享如何通过一系列优化手段,将一个典型 Spring Boot 应用的打包体积从 150MB 缩减至 30MB,缩减幅度达 80%。

目录


问题背景

典型场景

假设我们有一个标准的 Spring Boot Web 应用,包含以下依赖:

# 项目依赖概览dependencies:- spring-boot-starter-web - spring-boot-starter-data-jpa - spring-boot-starter-security - spring-boot-starter-validation - mysql-connector-java - lombok - commons-lang3 - guava - poi (Excel处理) - itext (PDF处理) 

执行 mvn clean package 后,得到一个 150MB 的 fat JAR:

target/ ├── application-1.0.0.jar (150MB) └── original-application-1.0.0.jar (30KB) 

为什么这么大?

让我们深入分析这个 150MB 的 JAR 包结构:

65%20%10%3%2%Spring Boot Fat JAR 体积构成分析第三方依赖库 [65]Spring Framework [20]业务代码 [2]嵌入式Tomcat [10]其他资源 [3]

分析结论

  • 第三方依赖 占据了 65% 的体积(97.5MB)
  • Spring Framework 自身占用 20%(30MB)
  • 业务代码 仅占 2%(3MB)
  • 嵌入式 Tomcat 占用 10%(15MB)

体积分析

1. 依赖分析工具

首先,我们需要找出哪些依赖占用空间最大。

Maven Dependency Analyzer
# 查看依赖树 mvn dependency:tree # 分析依赖大小 mvn com.github.ferstl:depgraph-maven-plugin:aggregate 
JAR 分析工具
# 使用 jar 命令查看内容 jar tf application.jar |head -20 # 解压后分析目录大小unzip -q application.jar -d app-extracted du -sh app-extracted/BOOT-INF/lib/* |sort -hr |head -10 
典型的大体积依赖
依赖体积说明
spring-boot-starter-web~15MB包含完整的 Web 模块
poi-ooxml~20MBExcel 处理全套组件
itextpdf~18MBPDF 生成库
tensorflow-core~50MBAI 模型(如有)
guava~8MBGoogle 工具库

2. 瘦身机会识别

Fat JAR 150MB

分析优化机会

移除未使用依赖
预期减少: 20-30MB

模块化拆分
预期减少: 40-50MB

启用分层构建
预期减少: 60-70MB

使用 Native Image
预期减少: 80-100MB

目标: 120MB

目标: 100MB

目标: 50MB

目标: 30MB


优化策略

策略一:依赖精简

1.1 排除传递依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 排除默认的 Tomcat,改用 Undertow --><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!-- Undertow 更轻量 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency></dependencies>
1.2 使用范围限定依赖
<dependencies><!-- 测试依赖仅在测试时使用 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Lombok 仅在编译时需要 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency></dependencies>
1.3 替换重型依赖
// 场景:Excel 导出功能// 方案 A:使用 Apache POI(重型,~20MB)importorg.apache.poi.ss.usermodel.*;importorg.apache.poi.xssf.usermodel.*;// 方案 B:使用 EasyExcel(轻量,~2MB)importcom.alibaba.excel.EasyExcel;importcom.alibaba.excel.write.metadata.WriteSheet;// 代码对比publicclassExcelExportExample{// EasyExcel 实现(内存占用更小)publicvoidexportLightweight(HttpServletResponse response){ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8");EasyExcel.write(response.getOutputStream(),UserData.class).sheet("用户数据").doFinish(()->{// 流式写入,内存友好});}}

策略二:分层 JAR 构建

2.1 Spring Boot Layered JARs

Spring Boot 2.3+ 支持分层 JAR,配合 Docker 可以极大优化镜像大小。

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><layers><enabled>true</enabled></layers></configuration></plugin></plugins></build>
2.2 分层结构说明

Layered JAR

Layer 1: 依赖

Layer 2: Spring Boot Loader

Layer 3: SNAPSHOT 依赖

Layer 4: 应用代码

Layer 5: 应用资源

变化频率: 极低

变化频率: 中等

变化频率: 高

优化原理:Docker 构建时,只有变化的层会被重新构建和推送,变化频率低的层可以长期复用缓存。

2.3 多阶段 Dockerfile
# 构建阶段 FROM maven:3.8-openjdk-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 运行阶段 - 使用精简的基础镜像 FROM eclipse-temurin:17-jre-alpine WORKDIR /app # 只复制必要的文件 COPY --from=builder /app/target/*.jar app.jar # 创建非 root 用户 RUN addgroup -S spring && adduser -S spring -G spring USER spring:spring EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] 

策略三:自定义类加载

3.1 外部依赖 JAR
/** * 外部依赖加载器 * 将依赖放在外部 lib/ 目录,减少主 JAR 体积 */publicclassExternalDependencyLauncher{publicstaticvoidmain(String[] args)throwsException{File libDir =newFile("lib"); URL[] jarUrls =Arrays.stream(Objects.requireNonNull(libDir.listFiles())).filter(file -> file.getName().endsWith(".jar")).map(File::toURI).map(uri ->{try{return uri.toURL();}catch(MalformedURLException e){thrownewRuntimeException(e);}}).toArray(URL[]::new);URLClassLoader classLoader =newURLClassLoader(jarUrls);Thread.currentThread().setContextClassLoader(classLoader);// 启动 Spring Boot 应用Class<?> appClass = classLoader.loadClass("com.example.Application");Method mainMethod = appClass.getMethod("main",String[].class); mainMethod.invoke(null,(Object) args);}}
3.2 Maven 配置
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifest><mainClass>com.example.ExternalDependencyLauncher</mainClass><classpathPrefix>lib/</classpathPrefix><addClasspath>true</addClasspath></manifest></archive></configuration></plugin><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></plugins></build>

策略四:GraalVM Native Image

4.1 原理说明

Java 源码

Maven 编译

Class 文件

GraalVM Native Image

原生可执行文件

传统 JVM

JVM 启动
~2-5秒

类加载
~1-3秒

JIT 编译
运行时

应用运行

Native Image

直接执行
~0.1秒

4.2 配置示例
<!-- pom.xml --><dependencies><dependency><groupId>org.springframework.experimental</groupId><artifactId>spring-native</artifactId><version>0.12.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.experimental</groupId><artifactId>spring-aot-maven-plugin</artifactId><version>0.12.0</version><executions><execution><id>test-generate</id><goals><goal>test-generate</goal></goals></execution><execution><id>generate</id><goals><goal>generate</goal></goals></execution></executions></plugin></plugins></build><profiles><profile><id>native</id><build><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>0.9.13</version><executions><execution><id>build-native</id><goals><goal>build</goal></goals><phase>package</phase></execution></executions></plugin></plugins></build></profile></profiles>
4.3 反射配置
// src/main/resources/META-INF/native-image/reflect-config.json[{"name":"com.example.model.User","allDeclaredConstructors":true,"allPublicConstructors":true,"allDeclaredMethods":true,"allPublicMethods":true,"allDeclaredFields":true,"allPublicFields":true},{"name":"com.example.controller.UserController","allDeclaredConstructors":true,"allDeclaredMethods":true}]

实战演示

完整优化配置

Maven 配置文件
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>optimized-spring-boot</artifactId><version>1.0.0</version><name>Optimized Spring Boot Application</name><properties><java.version>17</java.version><!-- 排除不必要的依赖 --><spring-boot-admin.version>3.1.0</spring-boot-admin.version></properties><dependencies><!-- Web 模块 - 使用 Undertow 替代 Tomcat --><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><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency><!-- 只引入需要的模块 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- 数据库 - 按需引入 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 驱动 - 使用 runtime scope --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- 工具类 - 选择轻量级替代品 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- Lombok - provided scope --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><!-- 测试依赖 - test scope --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><!-- Spring Boot 分层插件 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><layers><enabled>true</enabled></layers><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin><!-- 依赖分析插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><executions><execution><id>analyze</id><goals><goal>analyze</goal></goals></execution></executions></plugin></plugins></build></project>
优化后的 Dockerfile
# syntax=docker/dockerfile:1.4 # 多阶段构建 + BuildKit 缓存优化 # =================================== # 阶段 1: 依赖解析 (利用 Docker 缓存) # =================================== FROM maven:3.8-eclipse-temurin-17 AS deps WORKDIR /app # 先复制 pom.xml,下载依赖(这一层会被缓存) COPY pom.xml ./ RUN mvn dependency:go-offline -B # =================================== # 阶段 2: 编译 (代码变化时重新执行) # =================================== FROM maven:3.8-eclipse-temurin-17 AS builder WORKDIR /app # 从 deps 阶段复制本地仓库 COPY --from=deps /root/.m2 /root/.m2 # 复制源代码 COPY src ./src # 构建应用 RUN mvn clean package -DskipTests -B && \ mv target/*.jar app.jar # =================================== # 阶段 3: 运行时镜像 (最小化体积) # =================================== FROM eclipse-temurin:17-jre-alpine # 安装必要的工具和时区数据 RUN apk add --no-cache tzdata && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone && \ apk del tzdata WORKDIR /app # 创建非特权用户 RUN addgroup -S appgroup && \ adduser -S appuser -G appgroup # 复制应用 JAR COPY --from=builder --chown=appuser:appgroup /app/app.jar app.jar # 切换用户 USER appuser # JVM 参数优化 ENV JAVA_OPTS="-XX:+UseContainerSupport \ -XX:MaxRAMPercentage=75.0 \ -XX:InitialRAMPercentage=50.0 \ -XX:+UseG1GC \ -Djava.security.egd=file:/dev/./urandom" EXPOSE 8080 # 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] 
应用配置优化
# application.ymlspring:# 禁用不需要的自动配置autoconfigure:exclude:- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration # Web 优化web:resources:chain:cache:truestatic-locations: classpath:/static/ server:# Undertow 配置undertow:threads:io:4worker:20buffer-size:1024direct-buffers:true# 管理端点management:endpoints:web:exposure:include: health,info,metrics endpoint:health:show-details: always # 日志配置logging:level:root: INFO com.example: DEBUG pattern:console:"%d{yyyy-MM-dd HH:mm:ss} - %msg%n"

效果对比

优化前后对比表

指标优化前优化后改善幅度
JAR 包大小150 MB30 MB⬇️ 80%
Docker 镜像450 MB120 MB⬇️ 73%
启动时间8.5 秒2.3 秒⬇️ 73%
内存占用(运行时)512 MB256 MB⬇️ 50%
构建时间90 秒120 秒⬆️ 33%
注意:构建时间略有增加是正常的,因为增加了分层构建和优化步骤,但这是一次性成本。

体积优化分解图

35%25%20%10%10%各优化策略贡献分析依赖精简分层构建Undertow替换Native编译其他优化

不同方案适用场景

传统Web应用

微服务

云原生

选择优化方案

应用类型

方案1: 依赖精简
+ 分层构建
体积减少: 50%

方案2: 多阶段构建
+ 外部依赖
体积减少: 60%

方案3: Native Image
体积减少: 80%
启动时间减少: 90%

适用场景:
传统部署、虚拟机

适用场景:
Kubernetes、容器化

适用场景:
Serverless、边缘计算


最佳实践

1. 持续监控

# 定期检查依赖大小 mvn dependency:tree |grep -E "compile|runtime"# 使用 Maven 插件分析 mvn com.github.ferstl:depgraph-maven-plugin:aggregate 

2. 依赖管理规范

<!-- 在父 POM 中统一管理版本 --><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>3.2.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

3. CI/CD 集成

# .github/workflows/docker-build.ymlname: Build and Push Docker Image on:push:branches:[main]jobs:build:runs-on: ubuntu-latest steps:-uses: actions/checkout@v3 -name: Set up JDK 17 uses: actions/setup-java@v3 with:java-version:'17'distribution:'temurin'-name: Build with Maven run: mvn clean package -DskipTests -name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 -name: Login to Docker Hub uses: docker/login-action@v2 with:username: ${{ secrets.DOCKER_USERNAME }}password: ${{ secrets.DOCKER_PASSWORD }}-name: Build and push uses: docker/build-push-action@v4 with:context: . push:truetags: user/app:latest cache-from: type=gha cache-to: type=gha,mode=max build-args:| BUILDKIT_INLINE_CACHE=1

4. 避免的陷阱

陷阱说明解决方案
过度优化为了优化而优化,牺牲可维护性明确优化目标,权衡利弊
忽略测试删除"未使用"的依赖后测试失败完善测试覆盖,使用依赖分析工具
Native Image 兼容性反射、动态代理可能不工作提前测试,编写配置文件
安全隐患使用精简镜像可能缺少安全补丁定期更新基础镜像

总结

优化路线图

开始
Fat JAR 150MB

第一步
依赖分析

第二步
依赖精简
120MB

第三步
切换Undertow
110MB

第四步
分层构建
50MB

是否需要
极致优化?

第五步
Native Image
30MB

完成
50MB

完成
30MB

关键要点

  1. 先分析再优化:使用工具找出真正的体积大头
  2. 分步优化:不要一次性应用所有策略,逐步验证
  3. 权衡取舍:体积、性能、兼容性三者之间找平衡
  4. 团队协作:建立依赖管理规范,防止体积膨胀
  5. 持续改进:定期审查依赖,移除不再使用的库

参考资料


✍️ 坚持用清晰易懂的图解+可落地的代码,让每个知识点都简单直观!💡 座右铭:“道路是曲折的,前途是光明的!”

Read more

AI Agent 架构详解:感知-决策-执行的三位一体(基于最新研究论文)

AI Agent 架构详解:感知-决策-执行的三位一体(基于最新研究论文)

玄同 765 大语言模型 (LLM) 开发工程师 | 中国传媒大学 · 数字媒体技术(智能交互与游戏设计) ZEEKLOG · 个人主页 | GitHub · Follow 关于作者 * 深耕领域:大语言模型开发 / RAG 知识库 / AI Agent 落地 / 模型微调 * 技术栈:Python | RAG (LangChain / Dify + Milvus) | FastAPI + Docker * 工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案 「让 AI 交互更智能,让技术落地更高效」 欢迎技术探讨与项目合作,解锁大模型与智能交互的无限可能! AI Agent 架构详解:感知-决策-执行的三位一体 从 ReAct 到 Plan-and-Execute,从单智能体到多智能体协作,AI Agent

By Ne0inhk
openclaw安装gateway失败及openclaw重装

openclaw安装gateway失败及openclaw重装

解决方案 方法一:以管理员身份重新运行(最简单直接) 1. 完全关闭当前所有命令行窗口 2. 以管理员身份打开新的PowerShell或cmd: * 按 Win + S 搜索 “PowerShell” 或 “命令提示符” * 右键点击 → 选择 “以管理员身份运行” * 如果弹出UAC提示,点击"是" 安装成功后启动服务: openclaw-cn gateway start 直接运行安装命令(不需要切换目录): openclaw-cn gateway install 方法二:如果方法一不行,先解决编码问题再看真实错误 如果你想确认真实的错误信息,可以先切换编码: # 切换到UTF-8编码 chcp 65001# 然后重新运行安装命令(仍需要管理员权限) openclaw-cn gateway install 这样你就能看到真实的 拒绝访问 错误信息。 方法三:彻底卸载重装(

By Ne0inhk
Flutter 组件 okay 的适配 鸿蒙Harmony 深度进阶 - 驾驭异步结果链式融合、实现鸿蒙端分布式业务逻辑解耦与精密审计方案

Flutter 组件 okay 的适配 鸿蒙Harmony 深度进阶 - 驾驭异步结果链式融合、实现鸿蒙端分布式业务逻辑解耦与精密审计方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 okay 的适配 鸿蒙Harmony 深度进阶 - 驾驭异步结果链式融合、实现鸿蒙端分布式业务逻辑解耦与精密审计方案 前言 在前文中,我们探讨了 okay 在鸿蒙(OpenHarmony)端实现基础 Result 模式包装的实战。但在真正的“分布式微服务聚合”、“高并发资产对账”以及“具备自愈能力的 IoT 指令链”场景中。简单的 ok() 与 err() 判定往往不足以支撑起复杂的业务全景。面对需要同时并行发起 3 个 API 请求,并要求在“所有请求均成功时执行合并、任一请求失败时执行局部逻辑路由”的高阶需求。如果缺乏一套完善的异步结果映射与多级逻辑聚合机制。不仅会导致异步回调地狱(Callback Hell)在

By Ne0inhk
Spark+Flask新能源车数据分析与推荐系统实战:从0到1搭建完整项目

Spark+Flask新能源车数据分析与推荐系统实战:从0到1搭建完整项目

Spark+Flask新能源车数据分析与推荐系统实战:从0到1搭建完整项目 在碳中和政策驱动与新能源汽车产业爆发的双重背景下,车联网数据、用户行为数据、市场交易数据的价值愈发凸显。本文将带大家从零开始,基于Spark完成新能源车数据的分布式分析,再通过Flask搭建轻量级Web服务,结合协同过滤算法实现个性化车辆推荐,最终打造一套完整的新能源车数据分析与推荐系统。全程聚焦实战,所有代码均可直接复用,是大数据工程师、数据分析师的优质实战项目。 * Spark+Flask新能源车数据分析与推荐系统实战:从0到1搭建完整项目 * 一、项目整体架构与技术栈解析 * 1. 核心技术栈 * 2. 项目流程 * 二、实战步骤:从数据处理到推荐系统搭建 * 1. 环境准备 * 2. Spark数据处理实战 * (1)数据集说明 * (2)Spark数据清洗与分析 * (3)运行Spark脚本 * 3. 基于Spark MLlib实现协同过滤推荐 * 4. Flask搭建Web

By Ne0inhk