1. 简介
本文将介绍如何利用 spring-boot-thin-launcher 项目,将 Spring Boot 应用打包为 Thin JAR(瘦 JAR)。
众所周知,Spring Boot 默认采用“Fat JAR”(胖 JAR)部署方式 —— 即一个可执行的 JAR 包中包含了应用代码及其所有依赖。这种方式简单直接✅,但在微服务架构下容易造成资源浪费❌:多个服务重复打包相同的依赖库,占用大量存储和传输带宽。
而 Thin JAR 的核心思想是:只打包应用自身的代码,依赖在运行时动态解析和加载。这样可以显著减小发布包体积,提升 CI/CD 效率,特别适合容器化部署场景。
2. 前置条件
要使用 Thin JAR,你的项目必须是一个标准的 Spring Boot 项目。本文主要覆盖 Maven 和 Gradle 两种主流构建工具的配置方式。
⚠️ 注意:以下内容基于 Spring Boot 2.x 及以上版本。虽然 Thin Launcher 也支持更早版本,但 Gradle 配置略有不同,这里不再展开。
2.1 Maven 项目
确保你的 pom.xml
中已配置 Spring Boot 插件:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
并且通过 BOM 或父 POM 统一管理版本,例如:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/>
</parent>
这是标准做法,就不多解释了。
2.2 Gradle 项目
Gradle 项目需要引入 Spring Boot 插件和依赖管理插件:
buildscript {
ext {
springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin'
springBootVersion = '3.1.5'
}
repositories {
mavenCentral()
}
dependencies {
classpath("${springBootPlugin}:${springBootVersion}")
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
springBoot {
mainClassName = 'com.example.DemoApplication'
}
同样,这是典型的 Boot 项目结构。
3. Thin JAR 是如何工作的?
Spring Boot Thin Launcher 是一个轻量级启动器,它的作用是:
- 从 JAR 包中读取依赖声明(来自
pom.xml
或thin.properties
) - 通过 Maven 仓库下载缺失的依赖到本地缓存
- 动态构建类路径并启动应用主类
✅ 所以最终生成的 JAR 只包含:
- 你的业务代码
- 一个描述依赖的元数据文件
- Thin Launcher 的引导逻辑
运行时才去拉依赖,相当于把“打包体积”换成了“首次启动时间”,这个权衡在某些场景下非常值得。
4. 基本用法
和普通 Fat JAR 一样,启动命令仍然是:
java -jar my-app-1.0.jar
区别在于,这个 JAR 是“瘦”的,启动时会触发依赖解析流程。你也可以传入额外参数控制 Thin Launcher 行为,比如 --thin.dryrun=true
做预加载(后文会讲)。
4.1 Maven:启用 Thin JAR
只需在 spring-boot-maven-plugin
中添加一个依赖即可启用 Thin Layout:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<!-- 启用 Thin JAR 支持 -->
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.11.RELEASE</version>
</dependency>
</dependencies>
</plugin>
然后正常执行 mvn install
构建,生成的就是 Thin JAR。
✅ 小技巧:如果想同时支持 Fat 和 Thin 打包,可以把上述配置放在一个独立的 Maven Profile 中,按需激活。
4.2 Maven:生成 thin.properties
默认情况下,Thin Launcher 会读取 JAR 内嵌的 pom.xml
来解析依赖。但你也可以让 Maven 预先生成一个更完整的 thin.properties
文件,它包含所有直接和间接依赖,优先级高于 pom.xml
。
使用如下命令生成:
mvn org.springframework.boot.experimental:spring-boot-thin-maven-plugin:properties -Dthin.output=src/main/resources/META-INF
执行后会在指定目录生成 thin.properties
,内容类似:
#Generated by org.springframework.boot.experimental.maven.ThinPropertiesMojo
artifactId=my-app
groupId=com.example
version=1.0.0
...
⚠️ 注意:输出目录必须事先存在,否则命令会失败。
4.3 Gradle:启用 Thin JAR
Gradle 需要引入专用插件:
buildscript {
ext {
thinPlugin = 'org.springframework.boot.experimental:spring-boot-thin-gradle-plugin'
thinVersion = '1.0.11.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("${thinPlugin}:${thinVersion}")
}
}
apply plugin: 'maven'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
然后执行:
./gradlew thinJar
即可生成 Thin JAR。
4.4 Gradle:自定义 pom.xml
如前所述,Thin Launcher 依赖 pom.xml
解析依赖。Gradle 项目中,这个文件由 thinPom
任务生成,并自动成为 jar
任务的依赖。
如果你想自定义 pom.xml
内容,可以添加如下任务:
task createPom {
def basePath = 'build/resources/main/META-INF/maven'
doLast {
pom {
withXml(dependencyManagement.pomConfigurer)
}.writeTo("${basePath}/${project.group}/${project.name}/pom.xml")
}
}
并将其加入 bootJar
的依赖链:
bootJar.dependsOn = [createPom]
这样就能控制最终打包进 JAR 的 pom.xml
内容。
4.5 Gradle:生成 thin.properties
和 Maven 类似,Gradle 也能生成 thin.properties
替代 pom.xml
。
对应的任务是 thinProperties
,默认不启用。你需要手动将其加入构建依赖:
bootJar.dependsOn = [thinProperties]
之后构建时就会生成 thin.properties
文件。
5. 依赖的存储与缓存策略
Thin JAR 的本质是“延迟加载依赖”,这些依赖并不会消失,只是换了个地方存。Thin Launcher 使用标准 Maven 机制来解析依赖:
- ✅ 先查本地仓库(默认
~/.m2/repository
) - ❌ 缺失则从远程仓库(如 Maven Central)下载
- 💾 下载后缓存到本地,下次启动复用
显然,第 2 步是性能瓶颈和失败高发区——毕竟网络不可靠。为了避免每次启动都下载,有几种预加载方案。
5.1 预热运行(Warm-up)
最简单的办法是在目标环境先跑一次应用,把依赖提前下载好:
java -jar my-app-1.0.jar
但这样会真正启动应用,可能带来副作用(比如连接数据库、发消息等)。
✅ 推荐做法:使用 dry run(空跑)模式,只下载依赖不启动应用:
java -Dthin.dryrun=true -jar my-app-1.0.jar
或等价写法:
java -jar my-app-1.0.jar --thin.dryrun=true
✅ 支持 Spring Boot 风格的参数传递,
THIN_DRYRUN
环境变量也行,只要不是false
就生效。
5.2 构建时打包依赖(推荐)
更稳妥的方式是在 CI 构建阶段就把依赖下载好,和应用一起部署。
这样部署环境无需联网,启动更快更稳定。
依赖会被组织成标准 Maven 仓库结构:
deps/
repository/
com/
org/
net/
...
运行时通过 --thin.root
指定该目录:
java -jar my-app-1.0.jar --thin.root=./deps
多个应用的依赖可以安全合并(直接拷贝覆盖即可),形成一个共享仓库。
5.3 Maven:构建时下载依赖
使用 spring-boot-thin-maven-plugin
的 resolve
目标:
<plugin>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-maven-plugin</artifactId>
<version>1.0.11.RELEASE</version>
<executions>
<execution>
<id>resolve</id>
<goals>
<goal>resolve</goal>
</goals>
</execution>
</executions>
</plugin>
构建后会在 target/thin/root/
下生成完整的依赖树。
5.4 Gradle:构建时下载依赖
Gradle 使用 thinResolve
任务:
./gradlew thinResolve
执行后会在 build/thin/root/
目录生成依赖文件,结构与 Maven 一致。
部署时连同应用 JAR 一起拷贝过去,启动时用 --thin.root
指向该目录即可。
6. 总结与延伸阅读
Thin JAR 是一种轻量级部署方案,适合对包体积敏感的场景,比如:
- 微服务集群(减少镜像层重复)
- CI/CD 流水线(加快构建和传输)
- 资源受限环境
核心优势是“小”,代价是“首次启动慢”或“需预加载依赖”。合理使用 --thin.root
和 thin.dryrun
能很好平衡这一点。
📌 官方项目主页:https://github.com/dsyer/spring-boot-thin-launcher
包含更多实战指南,比如 Heroku 部署、Docker 集成等。
📌 示例代码:
- Maven 版本:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-bootstrap
- Gradle 版本:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-gradle
建议集合,踩坑时回来翻一翻。