欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

简易技巧:Maven打包Jar包瘦身100倍的方法

最编程 2024-02-19 19:44:57
...

为了大家能够从从项目集成的苦力活中解放出来,最近这一周,整理了两篇关于Jenkins构建、部署的文章,感兴趣的朋友可以看看:【基础教程】、【多模块构建

这期间,和粉丝大佬交流到关于Maven构建的优化、压缩的问题;一段简单的配置,就可以将包Jar包的大小缩小近百倍,实际的开发部署过程中,非常的实用的一个小技巧,在这里分享给各位;

SpringBoot项目的依赖,我们一般都会采用Maven管理,整个项目,一般都分为以下几部分:

  • 三方依赖通过pom.xml文件配置,添加到项目中来特点:变化小,占用空间大
  • 业务代码特点:变化大,占用空间小
  • 静态资源特点:变化适中,占用空间大;不过一般的静态资源都另外管理,很少会直接放在项目里面;

而整个项目通常会被构建成一个Jar,上传到服务器运行;整个Jar包中,三方依赖会被一并打包进去,占用空间最大的,也就是这部分依赖包;

比如下面这个最基本的测试 SpringBoot 项目,就一个简单的hello world接口,但是打包出来的jar就有20多M;

把Jar包解压之后,发现三方依赖竟然比整个Jar包都大(可能压缩的原因),自己的代码只有100多K;

这还只是一个最基础的项目,如果业务复杂,依赖多,光是三方包可能就占用几十、几百M之多;

由于依赖包变化小,占用空间大的特点,大部分情况是第一次添加之后,后面很少会去调整;但每次修改哪怕是一行代码,都需要重新把他们构建Jar中去,往服务器上传、发布,白白消耗了大量的资源、带宽以及时间;

那能否将三方依赖和自己的业务代码分开打包呢?答案是:可以的

1依赖拆分配置

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

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>
                <finalName>${project.build.finalName}</finalName>
                <!--解决windows命令行窗口中文乱码-->
                <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
                <layout>ZIP</layout>
                <includes>
                    <!--这里是填写需要包含进去的jar,
                        必须项目中的某些模块,会经常变动,那么就应该将其坐标写进来
                        如果没有则non-exists ,表示不打包依赖
                    -->
                    <include>
                        <groupId>non-exists</groupId>
                        <artifactId>non-exists</artifactId>
                    </include>
                </includes>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </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>
                        <excludeTransitive>false</excludeTransitive>
                        <stripVersion>false</stripVersion>
                        <includeScope>runtime</includeScope>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skip>true</skip>
            </configuration>
        </plugin>
    </plugins>
</build>

再次构建

mvn clean package -DskipTests=true

发现target目录中多了个lib文件夹,里面保存了所有的依赖jar,自己业务相关的jar也只有小小的157kb,相比之前21M,足足小了100多倍

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

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

虽然这样打包,三方依赖的大小并没有任何的改变,但有个很大的不同就是我们自己的业务包和依赖包分开了;在不改变依赖的情况下,也就只需要第一次上传lib目录到服务器,后续业务的调整、bug修复,在没调整依赖的情况下,就只需要上传更新小小的业务包即可;既省资源又省时间;就算是依赖变化了,也只需要更新调整的依赖,没变的依赖包咱也就不管了。

有朋友可能会说:你这业务包确实小了,但是无形中增加了对依赖包的管理,提高了管理成本

没错,这种方式,确实增加了Jar包的管理成本,多人协调开发,构建的时候,还需要专门去关注是否有人更新依赖;

不过这并不是啥大事儿,前面学习的Jenkins自动化工具,就能自动帮我们维护这个lib目录,减少人工核对,避免维护成本;


2Jenkins 管理依赖拆分的Jar

这一部分的内容依然是对前两篇关于Jenkins【基础教程】、【多模块构建】打包的完善,通过优化脚本,来实现 Jenkins 对依赖包、业务包的自动增量管理;

所以,这个并不是从0开始的教程,没看过前两篇文章的,可以先去扫一眼,然后再继续往下看;

SSH的方式

SSH的方式相比于之前的方式,只是多了管理lib中jar的过程,未调整的依赖Jar包,不上传到服务器;所以相比之前的方案,多了一个检测脚本jenkins_jar_and_lib_check.sh;他的作用就是在SSH上传之前,检测那些依赖更新了,然后只要留已更新的依赖上传到服务器;

Jenkins构建的过程

  • 拉取最新代码
  • Maven打包
  • Jenkins本地执行jenkins_jar_and_lib_check.sh检测依赖Jar和App jar是否更新
  • 上传已经更新的Jar/脚本
  • 远程执行jenkins_restart_mini.sh判断是否更新并重启,为了不影响之前的教程,这里新加了一个脚本jenkins_restart_mini.sh,和前面几篇文章中提到的jenkins_restart.sh作用是一样的
    • 判断依赖jar/业务jar是否更新(任意一个更新都需要重启)
    • 不需要更新的前提下,判断进程是否存在
    • 重启服务

jenkins_jar_and_lib_check.sh脚本

Jenkins本地校验踢出未更新的依赖和业务Jar的脚本;需要在Maven构建完,ssh传输之前使用;

完整脚本地址:github.com/vehang/ehan…

校验步骤:

  • 模块下创建一个tmp的临时目录
  • 将业务jar和依赖jar拷贝的tmp临时目录下
  • 分别对业务jar和依赖jar进行MD5校验
    • 更新的留下
    • 未更新的删除掉只有Jenkins本地的校验才删除,减少不必要的传输;服务端的检验不要删了,每一个都需要使用的;

脚本部分细节说明:

  • Jar包MD5校验方法:jar_check_md5()公共方法!直接通过Jar包的MD5校验是否更新# 直接通过jar校验
    jar_check_md5() {# jar 包的路径
    JAR_FILE=1if [!f1 if [ ! -f JAR_FILE ]; then

    如果校验的jar不存在 返回失败

    return 1
    fiJAR_MD5_FILE=JARFILE.md5echo "jenkins校验JARMD5文件:"{JAR_FILE}.md5echo "jenkins校验 JAR的MD5文件:"JAR_MD5_FILE
    if [ -f JARMD5FILE];thencatJAR_MD5_FILE ]; then cat JAR_MD5_FILEmd5sum JARFILEmd5sumstatuscJAR_FILEmd5sum --status -c JAR_MD5_FILERE=?md5sum?md5sum JAR_FILE > JARMD5FILEreturn JAR_MD5_FILEreturn RE
    else
    md5sum JARFILE>JAR_FILE > JAR_MD5_FILEfireturn 1
    }
  • Jar 解压校验文件详情jar_unzip_check_md5()公共方法!如果前面直接校验Jar的方式没有成功,就需要再通过解压的方式校验# 将Jar解压之后校验
    jar_unzip_check_md5() {# jar 包的路径
    UNZIP_JAR_FILE=1if [!f1 if [ ! -f UNZIP_JAR_FILE ]; then

    如果校验的jar不存在 返回失败

    return 1
    fi# jar的名称
    UNZIP_JAR_FILE_NAME=basename -s .jar $UNZIP_JAR_FILEecho "jenkins校验 JAR包名称:"$UNZIP_JAR_FILE_NAME

    jar所在的路径

    UNZIP_JAR_FILE_BASE_PATH={UNZIP_JAR_FILE%/{UNZIP_JAR_FILE_NAME}*}echo "jenkins校验 JAR包路径:"$UNZIP_JAR_FILE_BASE_PATH

    解压的临时目录

    JAR_FILE_UNZIP_PATH=UNZIPJARFILEBASEPATH/jarunziptmpecho "jenkins校验解压路径:"{UNZIP_JAR_FILE_BASE_PATH}/jar_unzip_tmpecho "jenkins校验 解压路径:"JAR_FILE_UNZIP_PATH

    用于缓存解压后文件详情的目录

    UNZIP_JAR_FILE_LIST=UNZIPJARFILEBASEPATH/{UNZIP_JAR_FILE_BASE_PATH}/{UNZIP_JAR_FILE_NAME}.filesecho "jenkins校验 jar文件详情路径:"$UNZIP_JAR_FILE_LIST

    缓存解压后文件详情的MD5

    UNZIP_JAR_FILE_LIST_MD5=UNZIPJARFILEBASEPATH/{UNZIP_JAR_FILE_BASE_PATH}/{UNZIP_JAR_FILE_NAME}.files.md5echo "jenkins校验 jar文件详情MD5校验路径:"UNZIPJARFILELISTrmrfUNZIP_JAR_FILE_LIST rm -rf JAR_FILE_UNZIP_PATHmkdir -p JAR_FILE_UNZIP_PATH# 解压文件到临时目录 unzip UNZIP_JAR_FILE -d JAR_FILE_UNZIP_PATH# 遍历解压目录,计算每个文件的MD5值及路径 输出到详情列表文件中 find JAR_FILE_UNZIP_PATH -type f -print | xargs md5sum > UNZIPJARFILELISTrmrfUNZIP_JAR_FILE_LIST rm -rf JAR_FILE_UNZIP_PATHif [ ! -f $UNZIP_JAR_FILE_LIST_MD5 ]; then

    如果校验文件不存在 直接返回校验失败

    md5sum UNZIPJARFILELIST>UNZIP_JAR_FILE_LIST > UNZIP_JAR_FILE_LIST_MD5return 1
    ficat UNZIPJARFILELISTMD5md5sumUNZIP_JAR_FILE_LIST_MD5md5sum UNZIP_JAR_FILE_LISTmd5sum --status -c UNZIPJARFILELISTMD5RE=UNZIP_JAR_FILE_LIST_MD5RE=?md5sum UNZIPJARFILELIST>UNZIP_JAR_FILE_LIST >