盘点工作的fat jar技术

45 阅读1分钟

在一年的Java后端开发工作中,参与和维护了不少项目,发现每个项目打包方式大相径庭,比如配置文件、依赖以及项目本体jar分开的,还有基于springboot fat jar的。由于前阵子开发了一个非spring工程的纯java项目,就使用了Shade fat jar技术。现在来做一下整理,对比下这两种fat jar打包方式的区别。

前置准备

项目结构如下:

├── pom.xml
└── src
    └── main
        ├── java
        │   └── cn
        │       └── pug
        │           └── fat
        │               └── jar
        │                   └── demo
        │                       └── Main.java
        └── resources
            └── config.yml

其中Main.java的逻辑为简单的读取classpath下的config.yml中的字符并打印

image.png

image.png

同时我们项目依赖下lombok,来观察下在fat jar中,依赖和配置是怎么样子存在的。

fat jar打包

spring boot fat jar

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.7.15</version>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <mainClass>cn.pug.fat.jar.demo.Main</mainClass>
    </configuration>
</plugin>

解压后目录树

├── BOOT-INF
│   ├── classes
│   │   ├── cn
│   │   │   └── pug
│   │   │       └── fat
│   │   │           └── jar
│   │   │               └── demo
│   │   │                   └── Main.class
│   │   └── config.yml
│   ├── classpath.idx
│   ├── layers.idx
│   └── lib
│       ├── lombok-1.18.4.jar
│       └── spring-boot-jarmode-layertools-2.7.15.jar
├── META-INF
│   ├── MANIFEST.MF
│   └── maven
│       └── cn.pug
│           └── fat-jar-demo
│               ├── pom.properties
│               └── pom.xml
├── fat-jar-demo-1.0.0.jar
└── org
    └── springframework
        └── boot
            └── loader
                ├── ...

shade fat jar

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.4</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>cn.pug.fat.jar.demo.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

解压后目录树

image.png

总结

Shade 插件打包出来的 fat jar 是标准的 Java 格式,将依赖的字节码文件全部拷贝到jar包根目录,配置文件也存于根目录,即根目录是classpath;而spring boot fat jar则将依赖的jar存放于BOOT-INF/lib下,本体项目的字节码以及配置文件存放于BOOT-INF/classes下,因此classpath对应这个目录;值得注意的是,spring boot fat jar其实篡改了主类,在MANIFEST.MF中即可看到,其篡改后的主类启动了一个自定义的类加载器用来加载BOOT-INF下的内容。