1. 简单的门面设计模式的使用
1.1 首先实现一个统一的门面的接口,实现一个Start 方法
package club.shengsheng.insight.input;
public interface ServerFacade {
void start() ;
}
1.2 具体的类继承这个接口,实现start方法
package club.shengsheng.insight;
import club.shengsheng.insight.input.ServerFacade;
public class MySQL implements ServerFacade {
void initData(){
System.out.println( "初始化MYSQL" );
}
void checkLog(){
System.out.println("校验日志,恢复可能没有提交的数据");
}
void unlock(){
System.out.println("释放锁");
}
void listenPort(){
System.out.println("监听端口");
}
@Override
public void start() {
this.initData();
this.checkLog();
this.unlock();
this.listenPort();
}
}
2 Maven 插件的API就是 maven 的门面,接下来使用门面模式 实现自定义插件的热加载
// 首先在一个项目中定义出插件的API
public interface MyPlugin {
void beforeGetTime() ;
}
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
目录如下
2.1 具体的插件工程 引入这个依赖,实现具体的插件逻辑
<?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>org.example</groupId>
<artifactId>count_plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>my_plugin_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2.2 实现这个依赖工程
工程架构
2.3 继承MyPlugin 接口 实现 方法
package org.example.insight;
import java.util.concurrent.atomic.AtomicInteger;
public class CountPlugin implements MyPlugin {
AtomicInteger count = new AtomicInteger(0);
@Override
public void beforeGetTime() {
System.out.println("CountPlugin beforeGetTime count: "+count.incrementAndGet() );
}
}
2.3 实现这个依赖工程 , 实现一个计数的逻辑
同时在resources的资源目录中写出相应的接口相对类路径
2.4 一个SpringBoot工程中引入这个计数插件的依赖,并实现一个http接口、使用反射实现对插件字节码的动态加载
@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private ThreadLocal<Integer> localInt;
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private MyPlugin plugin;
@GetMapping("/time")
public String getTime() {
if(plugin != null) {
plugin.beforeGetTime();
}
return LocalDate.now().format(dateTimeFormatter);
}
// 实现了我们插件的jar包,这个文件叫做 genyon.plugin 这个文件的内容就是实现myPlugin的全类名
// 加载插件的接口 count_plugin-1.0-SNAPSHOT.jar
// 门面设计模式
@GetMapping("/pluginLoad/{path}")
public String loadPlugin(@PathVariable("path") String path ) {
File file = new File(path);
try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{file.toURI().toURL()});
InputStream systemResourceAsStream = urlClassLoader.getResourceAsStream("genyon.plugin");) {
String fullClassName = new String(systemResourceAsStream.readAllBytes());
Class<?> abc = urlClassLoader.loadClass(fullClassName.trim());
Constructor<?> constructor = abc.getConstructor();
// 实现的my plugin的jar包的对象
plugin = (MyPlugin) constructor.newInstance();
return "记载成功: "+ plugin.toString();
}catch (Exception e) {
return "加载失败: "+ e.getMessage();
}
}
2.5 并将插件打包后放入 SpringBoot工程目录的相对路径中。
2.6 调用接口实现 动态加载,
1. 首先调用pluginLoad接口 实现 对插件的动态加载
结果如下
2. 调用相应的第一返回当前时间戳的接口,可以看到返回了时间戳前,还打印了计数插件的计数结果,说明插件顺利加载。
任何一个大的插件项目的原理都是如此,都是先定义一个插件,让第三方实现这个插件,我们通过约定将插件加载进来,并实现一些相应的功能。