思路
- plugin-face模块定义插件的接口
- plugin-applicaion模块是应用程序,插件的运行处,采用spring boot作为框架
- plugin-ly-initializer 一个具体实现的插件
部署运行的目录架构图
|-- plugin
|-- plugin-ly-initializer.jar
|-- 插件2.jar
|-- plugin-applicaion.jar
plugin-applicaion加载插件的思路, 读取具体实现插件的MANIFEST.MF文件的自定义字段
package ly.project.plugin.application.plugin;
import lombok.extern.slf4j.Slf4j;
import ly.project.face.plugin.PluginInitializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
@Component
@Slf4j
public class PluginRun {
//插件的存放路径
@Value("${plugin.path}")
private String pluginPath;
//插件
private Set<PluginInitializer> pluginInitializerSet = new HashSet<>();
//插件运行
public boolean run() throws FileNotFoundException {
log.info("开始加载插件,插件的加载路径为:{}", pluginPath);
//读取插件
File file = ResourceUtils.getFile(pluginPath);
log.info("是否为目录{}", file.isDirectory());
log.info("文件的绝对路径:{}", file.getAbsolutePath());
return Optional.ofNullable(file.listFiles()).map(pluginJars -> {
//初始化插件
for (File pluginJar : pluginJars) {
if (pluginJar.getName().endsWith(".jar")) {
try {
//获取启动类
JarFile jarFile = new JarFile(pluginJar);
Manifest mf = jarFile.getManifest();
Attributes mainAttributes = mf.getMainAttributes();
String startClass = mainAttributes.getValue("Ly-Plugin-Class");
if (startClass == null || startClass.equals("")) {
//manifest中没有自定义的Ly-Plugin-Class
continue;
}
//创建jar包中的插件,并且添加到pluginInitializerSet里面去
URL[] url = new URL[1];
url[0] = pluginJar.toURI().toURL();
log.info("打印具体的插件类:{}", startClass);
//重点!!!!在spring boot中,类加载一定要基于spring的类加载,不然会找不到类
URLClassLoader urlClassLoader = new URLClassLoader(url, SpringFactoriesLoader.class.getClassLoader());
Class<?> aClass = urlClassLoader.loadClass(startClass);
Object plugin = aClass.newInstance();
pluginInitializerSet.add((PluginInitializer) plugin);
} catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
log.error("错误信息:{}", e.getMessage());
}
}
}
//运行所有的插件
log.info("来了喔来了喔,插件来正在运行了喔");
pluginInitializerSet.forEach(pluginInitializer -> {
pluginInitializer.onInitialize();
});
return true;
}).orElse(false);
}
}
maven父子模块
父模块
- 父pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ly.project</groupId>
<artifactId>myJavaTest</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>thread</module>
<module>plugin-ly-initializer</module>
<module>plugin-application</module>
<module>plugin-face</module>
</modules>
<!-- 子模块相关依赖指定和dependencyManagement比较,parent必须一致,dependencyManagement可以特殊-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<!-- utf-8编码-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- dependencyManagement 标签,统一子模块公共依赖的版本,如果子模块有相同的依赖,并且没有指定具体版本,则采用该dependencyManagement定义-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>ly.project</groupId>
<artifactId>plugin-face</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
plugin-ly-initializer子模块
pom.xml文件;主要是把自定义键值对打包到MANIFEST.MF
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>myJavaTest</artifactId>
<groupId>ly.project</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>plugin-ly-initializer</artifactId>
<dependencies>
<dependency>
<groupId>ly.project</groupId>
<artifactId>plugin-face</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<!-- //追加一下MANIFEST.MF文件的自定义键值对-->
<archive>
<!--使用manifestFile属性配置自定义的参数文件所在的-->
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
参考链接
总结一些由于对java不熟,发生的异常问题
问题描述: 按照这两篇参考链接,编写完之后我能够在idea运行,编译、打包、运行;但是如果打包成jar,用命令行 java -jar xxx运行的时候,会抛出ClassNotFoundException这个异常;
关于类加载器 这个链接给了我思路
关键是在于 类加载器,一开始我是使用
new URLClassLoader(url);
一直报错,plugin-applicaion采用的是spring boot,spring boot 有自己特有的类加载,改成这样就好了
new URLClassLoader(url, SpringFactoriesLoader.class.getClassLoader());
要基于spring boot的类加载就好了
标题:java开发笔记之插件化开发&类加载器
作者:liangyu
地址:HTTPS://ly.laughingzhu.cn/articles/2021/06/24/1624537518696.html
Comments | 0 条评论