SpringBoot如何加载外部Jar来实现插件功能

实现方案

实现这个很简单,只需要完成下面两步:

  1. 想办法将class加载到JVM
  2. 使Spring扫描到class

为什么需要强调简单,个人感觉因为java中的各种框架大而全的框架以及面对对象的特性(主要继承)给我们造成了很大理解障碍,总感觉java框架源码很复杂晦涩难懂,其实是真的晦涩难懂哈哈。其实本质就这两步骤,没任何心理压力。

对于第一步,在《通过ClassLoader类加载器理解SpringBoot启动原理以及classpath》中我们从Java类加载原理讲到了SpringBoot启动以及类加载原理,那么此时就很容易想出下面三种方案了

  1. 扩大-cp由AppClassLoader帮我们加载外部jar
  2. 使用SpringBoot提供的启动参数loader.path实现
  3. 自定义classloader实现加载指定位置的jar

现在我们设定需要加载的jar在/user/local/java/plugins目录下,分别用几种方案实现

方案1:扩大-cp由AppClassLoader帮我们加载外部jar

可以使用 classpath 指定类加载的路径,但 classpath 的生效是有条件的,其实都可以理解为两种启动方式了:

命令classpath 生效说明
java -cp .;lib/x.jar Test运行 class
java -cp lib/x.jar -jar app.jar运行 jar

使用class启动-cp参数可以生效,那么SpringBoot的Main-Class是JarLauncher所以此时启动命令要改成

 

js

代码解读

复制代码

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 生效是有条件的:

整理了一份好像面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

命令MANIFEST.MF 的 Main-Classloader.path 生效打包配置
java -Dloader.path=./lib -jar app.jarJarLauncher默认配置
java -Dloader.path=./lib -jar app.jarPropertiesLauncher额外配置

loader.path 实现了 classpath 的功能。

配置 Main-Class

为了使用 loader.path,需要把 jar 包的 Main-Class 配置为 PropertiesLauncher,在 build.gradle 中如下配置,可参考 Using the PropertiesLauncher:

xml

代码解读

复制代码

<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可配置值如下:

  • JAR,即通常的可执行jar
    Main-Class: org.springframework.boot.loader.JarLauncher
  • WAR,即通常的可执行war,需要的servlet容器依赖位于WEB-INF/lib-provided
    Main-Class: org.springframework.boot.loader.WarLauncher
  • ZIP,即DIR,类似于JAR
    Main-Class: org.springframework.boot.loader.PropertiesLauncher
  • MODULE,将所有的依赖库打包(scope为provided的除外),但是不打包Spring Boot的任何Launcher
  • NONE,将所有的依赖库打包,但是不打包Spring Boot的任何Launcher

无论启动类是 JarLauncher 或者 PropertiesLauncher,loader.path 引入的 jar 和 Spring Boot 中 lib/*.jar 都是使用类加载器 org.springframework.boot.loader.LaunchedURLClassLoader 进行加载,也就是说他们使用的是同一个类加载器。

注意到同一个程序,打包成不同类型时,PropertiesLauncher (20s) 比 JarLauncher (8s) 启动慢很多。

方案3 自定义类加载器,加载固定目录下的jar包或者读取环境变量路径

这种方式需要程序启动的某个节点,调用自定义类加载器去加载指定目录下的jar包,时间点不是很好控制。但这个定制化程度比较高。

 

arduino

代码解读

复制代码

//可以在这里调用自定义类加载 SpringApplication.run(SpringBootDemo.class, args);

方案4 通过设置-Xbootclasspath/a或者 Extention ClassLoader通过改变参数java.ext.dirs实现

会改变系统类加载器加载行为不安全不推荐!!!

加载后的类如何扫描进入Spring容器

  • 对于业务jar包类,可以预先在SpringBoot中指定好扫描包路径,那么只需要插件包路径是com.demo开头即可,如下
 

typescript

代码解读

复制代码

@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:
 

ini

代码解读

复制代码

# 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值