适用场景
在项目中遇到需求,根据不同省份需要依赖不同的jar包执行相同逻辑。由于不同省份jar包不同,且存在同一jar包不同版本,导致高版本覆盖低版本的问题,同时为缩减项目包的大小。所以希望将这部分jar包通过外部引入的方式在运行时动态加载而非放入可执行jar包的libs目录下。这样项目分省部署只需依赖相关省份的jar包即可。
出现问题
基于此,根据maven的scope作用域(可参见之前博文),使用<scope>provideded</scope>
对这部分分省的jar包进行修饰。使系统通过编译,同时在执行时,根据省份不同以嵌入式的方式加载不同jar包。
但是发现<scope>provideded</scope>
修饰的jar在项目被打成可执行jar包时仍然会被打包到项目jar包的libs目录中。后来发现<scope>provideded</scope>
修饰的jar包在打成war包这种依赖容器(tomcat)执行的文件格式时时是不会被打包到libs目录中去的,但是打成可执行jar时不会起作用。
解决方法
使用spring-boot-maven-plugin插件将相关jar从libs移除,同时使用java -loader.path=jar包文件夹 -jar xxx.jar
方式在执行时动态引用。
spring-boot-maven-plugin的repackage指令可以将基于Spring构建的项目打成一个完整的可执行jar包,使其可以通过命令行,以java -jar的方式执行。其在打包过程中加入org.springframework.boot.loader,为基于Spring构建的项目添加引导(执行入口)。
基于spring-boot-maven-plugin解决本文问题。
pom.xml中添加:
<build>
<finalName>manage</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.czx.manage.App</mainClass> <!-- 项目main方法所在类 -->
<layout>ZIP</layout> <!-- 必填项,指定org.springframework.boot.loader使用的种类 -->
<excludes>
<exclude>
<groupId>com.xxxx</groupId>
<artifactId>xxxx</artifactId> <!-- 不希望打包到libs目录下的jar包 -->
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
这样在执行mvn package命令时,相关jar包不会被打到libs目录中。
spring boot maven plugin layout(spring-boot-maven-plugin layout)参数分析
spring-boot-maven-plugin官网对layout属性的陈述并不好理解。通过分析和实验发现其主要作用是指定项目启动的Main-Class的org.springframework.boot.loader的类型。其主要有JAR, WAR, ZIP, DIR, NONE 5中类型。默认是JAR类型。
通过观察打包后保存jar描述信息的MANIFEST.MF文件可以得知其对应关系:
- 不指定,默认
- JAR
- WAR
- ZIP和DIR
- NONE(打不可执行jar包)
如果要使用java -loader.path=jar包文件夹 -jar xxx.jar
以嵌入式的方式,在程序运行时引用外部jar包,需要指定org.springframework.boot.loader的类型为org.springframework.boot.loader.PropertiesLauncher。即使用DIR或ZIP方式。
如果项目以war包形式运行而非jar包形式,可以使用maven的scope设为provided的方式。若要以可执行jar包的方式运行,若需要适用这类场景,可参考本文实现方式。