- 背景
- 使用
- 工作原理
- 额外补充
- 运行应用程序进行预热
- 使用Maven在编译期打包依赖项
- 生产环境
背景
Spring Boot 默认打的Jar,包含应用程序代码及其所有依赖项(内置tomcat jar就不小了),所以打包出来的jar文件很大,动不动就几十,上百M,称之为Fat jar。
在网速不给力的情况下,上传服务器非常耗时。然而,其中我们引用到的Tomcat、Spring以及其他第三方组件,它们大部分时间是不会修改的而且占用了很大的空间,每次打包打进去。其实,我们经常改动的内容都是我们自己编写的代码,其大小大概也就几十KB,每次升级我们只需替换这些文件即可。
Spring社区大概也考虑到了开发者有这样的需求,所以提供了spring-boot-thin-launcher这个插件用来将项目的依赖和配置从jar包中分离出去。
使用
官网地址:https://github.com/spring-projects-experimental/spring-boot-thin-launcher
在Spring Boot pom文件中新增插件如下:
- <project ...>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot.experimental</groupId>
- <artifactId>spring-boot-thin-layout</artifactId>
- <version>1.0.27.RELEASE</version>
- </dependency>
- </dependencies>
- </plugin>
- </plugins>
- </build>
- </project>
然后还像以前一样执行mvn clean package,这时生成的jar包仅几十KB。
我这里因为项目有很多静态文件所以比较大有2MB。
执行java -jar xxx.jar即可直接运行程序。
除了jar文件减小了,其他效果看着与Fat jar是一样。
尝鲜之后,来看下其内部原理吧。
工作原理
我们来看下Jar包内部的构成。
- Manifest-Version: 1.0
- Implementation-Title: map
- Implementation-Version: 0.0.1-SNAPSHOT
- Start-Class: com.laker.map.LakerMapApplication
- Spring-Boot-Classes:
- Build-Jdk-Spec: 1.8
- Spring-Boot-Version: 2.3.7.RELEASE
- Created-By: Maven Jar Plugin 3.2.0
- Main-Class: org.springframework.boot.loader.wrapper.ThinJarWrapper
即启动类实际为:ThinJarWrapper
ThinJarWrapper类
我们编写的代码
项目的Pom文件
当执行java -jar xxx.jar时,实际执行的是ThinJarWrapper,它会先在指定目录搜索看看依赖的jar包是否都存在,存在则直接使用,如果不存在,先从Maven中央仓库下载到本地,然后,再执行我们自己编写的main()入口方法。这种方式有点类似很多在线安装程序:用户下载后得到的是一个很小的exe安装程序,执行安装程序时,会首先在线下载所需的若干巨大的文件,再进行真正的安装。
这个spring-boot-thin-launcher在启动时搜索的默认目录是用户主目录的.m2,我们也可以指定下载目录,例如,将下载目录指定为当前目录:
- java -Dthin.root=. -jar xxx.jar
执行后发现当前目录下自动生成了一个repository目录,这和Maven的默认下载目录~/.m2/repository的结构是完全一样的,只是它仅包含xxx.jar所需的运行期依赖项。
- repository/
- com/
- net/
- org/
- ...
“注意:只有首次运行时会自动下载依赖项,再次运行时由于无需下载,所以启动速度会大大加快。如果删除了repository目录,再次运行时就会再次触发下载。
额外补充
运行应用程序进行预热
缓存依赖项的最简单方法是在目标环境中对应用程序进行预热运行。正如我们之前看到的,这将导致依赖项被下载并缓存在本地 Maven 存储库中。如果我们运行多个应用程序,存储库最终将包含所有依赖项而没有重复项。
由于运行应用程序可能会产生不必要的副作用,我们还可以执行“试运行”,只解析和下载依赖项,而无需运行任何用户代码:
- java -Dthin.dryrun=true -Dthin.root=. -jar xxx.jar
使用Maven在编译期打包依赖项
添加以下依赖
- <plugin>
- <groupId>org.springframework.boot.experimental</groupId>
- <artifactId>spring-boot-thin-maven-plugin</artifactId>
- <version>${thin.version}</version>
- <executions>
- <execution>
- <!-- Download the dependencies at build time -->
- <id>resolve</id>
- <goals>
- <goal>resolve</goal>
- </goals>
- <inherited>false</inherited>
- </execution>
- </executions>
- </plugin>
构建项目后,目录为target/thin/root/。
生产环境
生产环境中,大部分都是内外网隔离的,建议先在本地“试运行”,然后把repository目录,瘦jar一起复制到服务器,设置thin.root指定目录,设置thin.offline切换到“离线”模式。所有依赖项都必须在本地可用.
- java -Dthin.root=. -Dthin.offline=true -jar xxx.jar
参考:
https://www.liaoxuefeng.com/wiki/1252599548343744/1304267002478625