在一年的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中的字符并打印
同时我们项目依赖下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>
解压后目录树
总结
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下的内容。