跳至主要內容

SpringBoot 工程部署的 jar 包瘦身

Jin大约 4 分钟

SpringBoot 工程部署的 jar 包瘦身

参考

1、需求背景

我们知道Spring Boot项目,是可以通过java -jar 包名 启动的。那为什么Spring Boot项目可以通过上述命令启动,而其它普通的项目却不可以呢?原因在于我们在通过以下命令打包时

mvn clean package

一般的maven项目的打包命令,不会把依赖的jar包也打包进去的,所以这样打出的包一般都很小,但Spring Boot项目的pom.xml文件中一般都会带有spring-boot-maven-plugin插件。该插件的作用就是会将依赖的jar包全部打包进去。该文件包含了所有的依赖和资源文件。也就会导致打出来的包比较大。打完包就可以通过java -jar 包名 启动,确实是方便了。但当一个系统上线运行后,肯定会有需求迭代和Bug修复,那也就免不了进行重新打包部署。

我们可以想象一种场景,线上有一个紧急致命Bug,你也很快定位到了问题,就改一行代码的事情,当提交代码并完成构建打包并交付给运维。

因为打包的jar很大,一直处于上传中....... 如果你来操作肯定会发火,就改了一行代码却上传几百MB的文件,难道没有办法优化一下吗?

如今迭代发布时常有的事情,每次都上传一个如此庞大的文件,会浪费很多时间。故而需要jar包瘦身。

2、瘦身原理

我们通过解压命令,看下jar的组成部分。

image-20231227173947555
image-20231227173947555
tar -zxvf spd-1.0.0.jar

我们可以看出,解压出来的包有三个模块

分为三个部分

  1. BOOT-INF
  2. META-INF
  3. org

打开 BOOT-INF

  1. classes: 当前项目编译好的代码是放在 classes 里面的,classes 部分是非常小的。
  2. lib: 我们所依赖的 jar 包都是放在 lib 文件夹下,lib部分会很大。
image-20231227174223841
image-20231227174223841

看了这个结构我们该如何去瘦身呢?项目虽然依赖会很多,但是当版本迭代稳定之后,依赖基本就不会再变动了。如果可以把这些不变的依赖提前都放到服务器上,打包的时候忽略这些依赖,那么打出来的Jar包就会小很多,直接提升发版效率。当然这样做你肯定有疑问?

既然打包的时候忽略这些依赖,那通过java -jar 包名 还可以启动吗?这种方式打的包,在项目启动时,需要通过-Dloader.path指定lib的路径,就可以正常启动

Linux环境

java -Dloader.path=./lib -jar xxx.jar

Windows环境

java -Dloader.path=.\spd-lib -Dfile.encoding=utf-8 -Xmx768m -Xms256m -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=80 -jar
        %JAR_NAME%.jar --server.port=9528

3、瘦身实例演示

3.1、依赖拆分配置

只需要在项目pom.xml文件中添加下面的配置:

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                	<!--<includeSystemScope>false</includeSystemScope>-->
                    <executable>true</executable>
                    <layout>ZIP</layout>
                    <!--这里是填写需要包含进去的jar,
          必须项目中的某些模块,会经常变动,那么就应该将其坐标写进来
          如果没有则nothing ,表示不打包依赖 -->
                    <includes>
                        <include>
                            <groupId>nothing</groupId>
                            <artifactId>nothing</artifactId>
                        </include>
                    </includes>
                </configuration>
            </plugin>

            <!--拷贝依赖到jar外面的lib目录-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!--指定的依赖路径-->
                            <outputDirectory>
                                ${project.build.directory}/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

再次打包

mvn clean package

这种方式打的包,在项目启动时,需要通过-Dloader.path指定lib的路径:

java -Dloader.path=.\jin-lib -Dfile.encoding=utf-8 -Xmx768m -Xms256m -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=80 -jar
        %JAR_NAME%.jar --server.port=9528

虽然这样打包,三方依赖的大小并没有任何的改变,但有个很大的不同就是我们自己的业务包依赖包分开了;

在不改变依赖的情况下,也就只需要第一次上传lib目录到服务器,后续业务的调整、bug修复,在没调整依赖的情况下,就只需要上传更新小小的业务包即可;

4、依赖自己公司的其它模块如何处理?

我们在做项目开发时,除了会引用第三方依赖,也会依赖自己公司的其它模块。这种依赖自己其它项目的工程,也是会经常变动的,所以不宜打到外部的lib,不然就会需要经常上传更新。那怎么做了? 其实也很简单 只需在上面的插件把你需要打进jar的填写进去就可以了

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <executable>true</executable>
        <layout>ZIP</layout>
        <!--这里是填写需要包含进去的jar,如果没有则nothing -->
            <includes>
                <include>
                    <groupId>com.jin</groupId>
                    <artifactId>jin-util</artifactId>
                </include>
            </includes>
    </configuration>
</plugin>

这样只有include中所有添加依赖依然会打进当前业务包中。

贡献者: Jin