pf4j及pf4j-spring
什么是PF4J
一个插件框架,用于实现插件的动态加载,支持的插件格式(zip、jar)。
核心组件
- Plugin:是所有插件类型的基类。每个插件都被加载到一个单独的类加载器中以避免冲突。
- PluginManager:用于插件管理的所有方面(加载、启动、停止)。您可以使用内置实现作为JarPluginManager, ZipPluginManager, DefaultPluginManager(它是一个JarPluginManager+ ZipPluginManager),或者您可以从AbstractPluginManager(仅实现工厂方法)开始实现自定义插件管理器。
- PluginLoader:加载插件所需的所有信息(类)。
- ExtensionPoint:是应用程序中可以调用自定义代码的点。这是一个java接口标记。任何 java 接口或抽象类都可以标记为扩展点(实现ExtensionPoint接口)。
- Extension:是扩展点的实现。它是一个类上的 Java 注释。
使用示例
Demo整体架构
- Plugin-api:定义可扩展接口。之后所有的扩展接口可以放到一个单独的 plugin-core 模块中,然后打成jar包,放到主程序 plugin-app 中。
- Plugins:插件项目,可以包含多个插件,需要实现 plugin-api 中定义的接口。所有的插件jar包,放到统一的文件夹中,方便管理,后续只需要加载文件目录路径即可启动插件。
- plugin-app:主程序,需要依赖 plugin-api ,加载并执行 plugins 。
导入依赖
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>3.0.1</version>
</dependency>
自定义扩展接口,集成 ExtensionPoint ,标记为扩展点
public interface Greeting extends ExtensionPoint {
String getGreeting();
}
使用 @Extension注解 自定义类扩展类,实现扩展接口
@Extension
public class WelcomeGreeting implements Greeting { public String getGreeting() {
return "Welcome";
} }
如果你想要能够控制插件的生命周期,你可以自定义类集成 plugin 重新里面的方法
public class WelcomePlugin extends Plugin {
public WelcomePlugin(PluginWrapper wrapper) {
super(wrapper);
// you can use "wrapper" to have access to the plugin context (plugin manager, descriptor, ...)
}
@Override
public void start() {
System.out.println("WelcomePlugin.start()");
}
@Override
public void stop() {
System.out.println("WelcomePlugin.stop()");
}
@Override
public void delete() {
System.out.println("WelcomePlugin.delete()");
}
}
使用 MANIFEST.MF 记录插件的信息
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: decebal
Build-Jdk: 1.6.0_17
Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin
Plugin-Dependencies: x, y, z
Plugin-Id: welcome-plugin
Plugin-Provider: Decebal Suiu
Plugin-Version: 0.0.1
主程序启动
public static void main(String[] args) {
...
// create the plugin manager
PluginManager pluginManager = new JarPluginManager(); // or "new ZipPluginManager() / new DefaultPluginManager()"
// start and load all plugins of application
pluginManager.loadPlugins();
pluginManager.startPlugins();
// retrieve all extensions for "Greeting" extension point
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
for (Greeting greeting : greetings) {
System.out.println(">>> " + greeting.getGreeting());
}
// stop and unload all plugins
pluginManager.stopPlugins();
pluginManager.unloadPlugins();
...
}
输出
>>> Welcome
更多
https://github.com/pf4j/pf4j
spring整合PF4J
核心组件
ExtensionsInjector :允许 PF4J 的扩展作为 Spring bean 公开。
SpringPlugin :如果您的插件包含 Spring bean,则SpringPlugin您的插件会扩展此类。
SpringExtensionFactory :如果你有SpringPlugins使用此ExtensionFactory在插件管理。
SpringPluginManager :一个 Spring 感知 PluginManager。
使用示例
引入依赖
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-spring</artifactId>
<version>${pf4j-spring.version}</version>
</dependency>
这里的版本号,你可以去maven仓库里拿最新的,也可以在pom文件里添加下面的代码取最新的。
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
建议使用 0.6.0 的版本,0.7.0 好像有点问题,在扩展配置类的时候。
PF4J-SPRING 和 PF4J 使用起来其实差不多,如果使用了Spring框架的话,我们就要写一个配置类,这里面用来定义 pluginManager 管理插件,有兴趣的可以看下源码,其实底层都是用的 PF4J的东西,只是封装了一层。
@Configuration
public class SpringConfiguration { @Bean
public SpringPluginManager pluginManager() {
return new SpringPluginManager();
} @Bean
@DependsOn("pluginManager")
public Greetings greetings() {
return new Greetings();
} }
public class Greetings {
@Autowired
private List<Greeting> greetings;
public void printGreetings() {
System.out.println(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName()));
for (Greeting greeting : greetings) {
System.out.println(">>> " + greeting.getGreeting());
}
}
}
package org.pf4j.demo; import org.apache.commons.lang.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.pf4j.PluginManager; /**
* A boot class that start the demo.
*
* @author Decebal Suiu
*/
public class Boot { public static void main(String[] args) {
// 启动PF4J-SPRING
printLogo(); // 加载自定义的配置类,jar包加载控制器
// 这一步会先全局扫描插件,没有找到插件的话,就会找可能的extensions
/*
Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@cac736f
Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
Creating shared instance of singleton bean 'springConfiguration'
Creating shared instance of singleton bean 'pluginManager'
INFO org.pf4j.DefaultPluginStatusProvider - Enabled plugins: []
INFO org.pf4j.DefaultPluginStatusProvider - Disabled plugins: []
INFO org.pf4j.DefaultPluginManager - PF4J version 3.5.0 in 'deployment' mode
DEBUG org.pf4j.AbstractPluginManager - Lookup plugins in '[plugins]'
WARN org.pf4j.AbstractPluginManager - No 'plugins' root
INFO org.pf4j.AbstractPluginManager - No plugins
DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storages from classpath
DEBUG org.pf4j.LegacyExtensionFinder - Read '/Users/lihui/Documents/Java/pf4j-spring/pf4j-spring/demo/app/target/classes/META-INF/extensions.idx'
DEBUG org.pf4j.LegacyExtensionFinder - Read '/Users/lihui/Documents/Java/pf4j-spring/pf4j-spring/pf4j-spring/target/classes/META-INF/extensions.idx'
DEBUG org.pf4j.LegacyExtensionFinder - Read '/Users/lihui/Documents/Java/pf4j-spring/pf4j-spring/demo/api/target/classes/META-INF/extensions.idx'
DEBUG org.pf4j.AbstractExtensionFinder - Found possible 1 extensions:
DEBUG org.pf4j.AbstractExtensionFinder - org.pf4j.demo.WhazzupGreeting
DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storages from plugins
DEBUG org.pf4j.spring.ExtensionsInjector - Register extension 'org.pf4j.demo.WhazzupGreeting' as bean
DEBUG org.pf4j.spring.SpringExtensionFactory - Extension class ' org.pf4j.demo.WhazzupGreeting' belongs to a non spring-plugin (or main application) 'system, but the used PF4J plugin-manager is a spring-plugin-manager. Therefore the extension class will be autowired by using the managers application contexts
DEBUG org.pf4j.spring.SpringExtensionFactory - Instantiate extension class 'org.pf4j.demo.WhazzupGreeting' by using constructor autowiring.
DEBUG org.pf4j.spring.SpringExtensionFactory - Completing autowiring of extension: org.pf4j.demo.WhazzupGreeting@363ee3a2
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'greetings'
*/
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class); // retrieves automatically the extensions for the Greeting.class extension point
// 自动检索Greet.class扩展点的扩展名
Greetings greetings = applicationContext.getBean(Greetings.class);
greetings.printGreetings(); // stop plugins
PluginManager pluginManager = applicationContext.getBean(PluginManager.class);
/*
// retrieves manually the extensions for the Greeting.class extension point
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
System.out.println("greetings.size() = " + greetings.size());
*/
pluginManager.stopPlugins();
} private static void printLogo() {
System.out.println(StringUtils.repeat("#", 40));
System.out.println(StringUtils.center("PF4J-SPRING 已启动", 40));
System.out.println(StringUtils.repeat("#", 40));
} }
这里有两种使用的方式,具体的使用你可以参考官方给的Demo例子。
同样的,你如果想要控制插件的生命周期,自定义实现类继承SpringPlugin就好了。
public class HelloPlugin extends SpringPlugin {
public HelloPlugin(PluginWrapper wrapper) {
super(wrapper);
}
@Override
public void start() {
System.out.println("HelloPlugin.start()");
}
@Override
public void stop() {
System.out.println("HelloPlugin.stop()");
super.stop(); // to close applicationContext
}
@Override
protected ApplicationContext createApplicationContext() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
applicationContext.register(SpringConfiguration.class);
applicationContext.refresh();
return applicationContext;
}
更多
https://github.com/pf4j/pf4j-spring
pf4j及pf4j-spring的更多相关文章
- pf4j实例 插件框架
实现整个过程需要三个部分,第一就是根接口,第二是插件,第三是应用程序.这是3个java项目. 首先要下载jar包,百度搜索maven repository,然后搜索pf4j,如下图,下载第一个的相应版 ...
- PF4J使用
PF4J是一个Java轻量级的插件框架,可以实现动态加载,执行,卸载外部插件(支持jar以及zip),具体可以看官网:https://pf4j.org/. 本文例子基于Github地址:https:/ ...
- 基于spring注解AOP的异常处理
一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...
- 玩转spring boot——快速开始
开发环境: IED环境:Eclipse JDK版本:1.8 maven版本:3.3.9 一.创建一个spring boot的mcv web应用程序 打开Eclipse,新建Maven项目 选择quic ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- [Spring]IoC容器之进击的注解
先啰嗦两句: 第一次在博客园使用markdown编辑,感觉渲染样式差强人意,还是github的样式比较顺眼. 概述 Spring2.5 引入了注解. 于是,一个问题产生了:使用注解方式注入 JavaB ...
- 学习AOP之透过Spring的Ioc理解Advisor
花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...
- 学习AOP之深入一点Spring Aop
上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...
- 学习AOP之认识一下Spring AOP
心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...
随机推荐
- [bzoj1079]着色方案
由于最终的染色只与ci为几的个数有关,因此定义状态f[a][b][c][d][e][p]表示有a个ci=1,b个ci=2,--,有e个ci=5,上一次选择了ci=p的.状态的转移:发现p会让p-1少选 ...
- 【JVM源码解析】模板解释器解释执行Java字节码指令(上)
本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...
- Dirichlet 前缀和的几种版本
[模板]Dirichlet 前缀和 求 \[B[i] = \sum_{d|i} A[d] \] $ n \le 2\times 10^{7} $ 看代码: for( int i = 1 ; i < ...
- Codeforces 1236F - Alice and the Cactus(期望+分类讨论)
Codeforces 题面传送门 & 洛谷题面传送门 期望好题. 首先拆方差: \[\begin{aligned} &E((x-E(x))^2)\\ =&E(x^2)-2E(x ...
- vim 的使用
基本操作: 命令行模式 进入命令行 打开文本的时候,直接进去命令行模式 在其它模式按ESC,可以进入命令行模式 新建进入了命令行模式 光标进入末行"G"(shift+按键g,自学 ...
- 一文搞懂指标采集利器 Telegraf
作者| 姜闻名 来源|尔达 Erda 公众号 导读:为了让大家更好的了解 MSP 中 APM 系统的设计实现,我们决定编写一个<详聊微服务观测>系列文章,深入 APM 系统的产品.架构 ...
- day09 orm查询优化相关
day09 orm查询优化相关 今日内容概要 orm字段相关补充 orm查询优化相关 orm事务操作 图书管理系统练习 今日内容详细 orm事务操作 """ 事务:ACI ...
- 【Reverse】每日必逆0x01
附件:https://files.buuoj.cn/files/7458c5c0ce999ac491df13cf7a7ed9f1/SimpleRev 题解 查壳 64位ELF文件,无壳 IDApro处 ...
- 【leetocde】922. Sort Array By Parity II
Given an array of integers nums, half of the integers in nums are odd, and the other half are even. ...
- 容器之分类与各种测试(四)——multiset
multiset是可重复关键字的关联式容器,其与序列式容器相比最大的优势在于其查找效率相当高.(牺牲空间换取时间段) 例程 #include<stdexcept> #include< ...