Spring Boot 项目常规打 JAR 包时,是将应用本身及所有依赖打到同一个 *.jar 文件中。在部署时,只要将这个文件拷贝到生产环境,就可以使用 java -jar *.jar 这样的简单命令运行应用,非常方便。
这个 jar 文件包含了所有依赖,因此体积较大,常在 50MB 以上,在有些传输受限的部署环境中(比如政务外网),上传大文件会比较耗时。
通过一些技巧,将应用自身的编译结果单独打包,形成体积较小的 jar 文件,与较大的依赖包分开传输、增量更新部署,是解决上述问题的一个思路。 这种打包方式被形象的称之为 “瘦包” -Thin JAR,相对的常规打包方式称为 “肥包” - Fat JAR。
Spring Boot 官方开发了一个子项目来帮助解决这个问题:spring-boot-thin-launcher
这个项目尚处于试验阶段,当前已发布了 v1.0.28.RELEASE 版本。
用一个 gradle 示例简单展示一下。
利用 Spring 官网在线工具 start.spring.io/ 创建一个 demo 工程,添加 "Spring Web" 作为依赖。
在 build.gradle 中添加插件 org.springframework.boot.experimental.thin-launcher
plugins {
id 'org.springframework.boot' version '2.7.3'
id 'io.spring.dependency-management' version '1.0.13.RELEASE'
id 'java'
id 'org.springframework.boot.experimental.thin-launcher' version '1.0.28.RELEASE'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
这个插件会给工程的 Gradle 构建添加了几个 thin* 开头的任务,并将默认 build 任务打 Fat JAR 的行为调整为打 Thin JAR。
此时运行命令 gradlew build 就会在工程 build/libs/ 目录中输出瘦包。
这个瘦包不是简单的对应用编译成果的组装,插件对瘦包中的启动类做了调整,加入了在启动时准备所需依赖包的逻辑,使用起来会很方便。
执行
java -jar demo-0.0.1-SNAPSHOT.jar
插件加入的启动类会自行下载所需的依赖包,存放到 Maven 默认本地仓库目录 ~/.m2/repository/ 中,并以这个目录为基础配置好 CLASSPATH,以便正常运行 demo-0.0.1-SNAPSHOT.jar 应用。
第一次启动需要下载依赖包,需耐心等待依赖包下载完成,后续启动就很快了。如果第一次下载包中途因为网络或其它原因异常中断了,需要清理
~/.m2/repository/目录后重新执行。
如果能在部署环境中正常访问 Maven 包仓库(包括互联网仓库或私有仓库),这种方法很方便就能让瘦包在部署环境中跑起来,而且后续应用升级也只需要更新瘦包就可以。
如果不能访问 Maven 包仓库,还可以利用插件提供的 thinResolve 任务来将依赖包预先准备好。
在 IDE 中执行任务
gradlew thinResolve
或使用命令行,
cp build/libs/demo-0.0.1-SNAPSHOT.jar build/thin/root/
cd build/thin/root
java -jar demo-0.0.1-SNAPSHOT.jar --thin.root=.
耐心等待一段时间,会在工程 build/thin/root/ 目录下生成瘦包并下载依赖包。
将此目录传输到生产环境,同样通过指定参数 thin.root=. 告诉插件的启动类在哪里找依赖包,就能把应用跑起来。
java -jar demo-0.0.1-SNAPSHOT.jar --thin.root=.
为了加速依赖包下载,可以在 Maven ~/.m2/settings.xml 中加入阿里云镜像。(但实测依然不是很快)
上面 demo 已在 Gradle 7.3.1/7.5 版本,以及 Spring Boot 2.6.6/2.7.3 中实测通过。
推荐阅读:
-
spring-boot-thin-launcher 官网 README,有更多的介绍。
-
官方代码库 github.com/spring-proj… 中也有些示例,其中 "multi" 展示了有多个子项目的 monorepo 工程中的用法。