实现方案
实现这个很简单,只需要完成下面两步:
- 想办法将 class 加载到 jvm
- 使 spring 扫描到 class
有时候java中的各种框架以及复杂的概念源码给我们造成了很大理解障碍,特别是spring家族的所以先想清楚大致实现步骤比较好
将class加载到jvm
现在我们设定需要加载的 jar 在 /user/local/java/plugins
目录下,对于第 1 步,现在有如下几种实现方式:
- 扩大
-cp
由appclassloader
帮我们加载外部 jar - 使用 springboot 提供的启动参数
loader.path
实现 - 自定义
classloader
实现加载指定位置的 jar
其中 1 和 3 是jdk提供的方案,2是 springboot 扩展的方案,毕竟 springboot 也需要自定义加载很多类顺带扩展了一个参数出来
方案1
扩大 -cp 由 appclassloader 帮我们加载外部 jar
可以使用 classpath
指定类加载的路径,但 classpath
的生效是有条件的,这里其实对应了 jar 包的两种启动方式:通过jar包里的main函数启动或者运行jar启动
springboot 打包后 manifest.mf
内容如下:
manifest-version: 1.0 created-by: maven jar plugin 3.2.2 build-jdk-spec: 18 implementation-title: intelligent-soul implementation-version: 0.0.1-snapshot main-class: org.springframework.boot.loader.jarlauncher start-class: com.test.myapplication spring-boot-version: 2.6.15 spring-boot-classes: boot-inf/classes/ spring-boot-lib: boot-inf/lib/ spring-boot-classpath-index: boot-inf/classpath.idx spring-boot-layers-index: boot-inf/layers.idx
所以需要使用 main 函数启动的方式,不能使用之前的常用的 jar 包启动方式了。对于 springboot 应用 main-class
是 jarlauncher
所以此时启动命令要改成
java -cp /user/local/java/plugins org.springframework.boot.loader.jarlauncher
- 优点:实现简单
- 缺点:每个 jar 包启动命令都一样了,不好区分
方案2
使用springboot扩展的启动参数,使用java -jar 配合loader.path参数
因为 spring boot 程序大多是打成 jar 包,使用 java -jar boot.jar
的方式启动 (此时 -cp
无效),可以使用 loader.path
指定类加载路径加载其他 jar,但 loader.path
生效也是有条件的:
springboot 默认打包方式的main-class
是jarlauncher
配置 main-class
为了使 loader.path
参数生效需要生成 jar 包的 main-class
项配置为 propertieslauncher
,在 maven
中如下配置,可参考 using the propertieslauncher
:
<build> <plugins> <plugin> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-maven-plugin</artifactid> <configuration> <!-- 打包时排除scope为system的包 --> <includesystemscope>false</includesystemscope> <!-- jvm启动时通过-dloader.path加载包,必须指定layout为zip,否则-dloader.path无效!!! --> <layout>zip</layout> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> <finalname>test</finalname> </build>
其中 layout 可配置值如下:
1、jar,即通常的可执行 jar
main-class: org.springframework.boot.loader.jarlauncher
2、war,即通常的可执行war,需要的 servlet 容器依赖位于 web-inf/lib-provided
main-class: org.springframework.boot.loader.warlauncher
3、zip,即dir,类似于 jar
main-class: org.springframework.boot.loader.propertieslauncher
4、module,将所有的依赖库打包(scope
为provided
的除外),但是不打包 spring boot 的任何launcher
5、none,将所有的依赖库打包,但是不打包 spring boot 的任何 launcher
无论启动类是 jarlauncher
或者 propertieslauncher
,loader.path
引入的 jar 和 spring boot jar 包中 boot-info/lib
包下 jar 都是使用类加载器 org.springframework.boot.loader.launchedurlclassloader
进行加载,也就是说他们使用的是同一个类加载器。这个类加载器主要就是负责加载 jar 包里的 jar 的加载,因为jdk没有实现该功能所以 springboot 定义了该类
注意到同一个程序,打包成不同类型时,propertieslauncher (20s)
比 jarlauncher (8s)
启动慢很多。
方案3
自定义类加载器,加载固定目录下的jar包或者读取环境变量路径
这种方式需要程序启动的某个节点,调用自定义类加载器去加载指定目录下的 jar 包,时间点不是很好控制。但这个定制化程度比较高。
//可以在这里调用自定义类加载 springapplication.run(springbootdemo.class, args);
方案4
通过设置-xbootclasspath/a或者 extention classloader通过改变参数java.ext.dirs实现
会改变系统类加载器加载行为不安全不推荐!!!
加载后的类如何扫描进入spring容器
对于业务 jar 包类,可以预先在springboot中指定好扫描包路径,那么只需要插件包路径是com.demo
开头即可,如下
@springbootapplication(scanbasepackages = {"com.demo"}) public class springbootdemoapplication { public static void main(string[] args) { springapplication.run(springbootdemoapplication.class, args); }
如不符合包名前缀一致的情况,需要在jar包meta-inf
下的spring.factories
指定好自动装配的类即可如 mybatis-plus:
# auto configure org.springframework.boot.env.environmentpostprocessor=\ com.baomidou.mybatisplus.autoconfigure.safetyencryptprocessor org.springframework.boot.autoconfigure.enableautoconfiguration=\ com.baomidou.mybatisplus.autoconfigure.identifiergeneratorautoconfiguration,\ com.baomidou.mybatisplus.autoconfigure.mybatispluslanguagedriverautoconfiguration,\ com.baomidou.mybatisplus.autoconfigure.mybatisplusautoconfiguration
到此这篇关于springboot加载外部jar实现功能按需扩展的文章就介绍到这了,更多相关springboot加载外部jar内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论