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,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...
随机推荐
- [hdu5901]Count primes
最简单的是利用Min25筛求$h(n)$的过程,即 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1000005 ...
- 还有这种好事!netty自带http2的编码解码器framecodec
目录 简介 Http2FrameCodec Http2Frame.Http2FrameStream和Http2StreamFrame Http2FrameCodec的构造 Stream的生命周期 流控 ...
- IPv6 寻址方式简介
在计算机网络中,寻址模式是指在网络上托管地址的机制.IPv6 提供了多种类型的模式,可以通过这些模式对单个主机进行寻址.也可以同时对多个主机进行寻址或者寻址最近距离的主机. 单播寻址 在单播寻址方式 ...
- 超算云(GPU服务器)环境配置
最近在用并行超算云GPU服务器(中国国家网格12区)搭建毕设的环境,这里记录一下. 首先,超算云服务器的登录可以采用网页版.也可以采用客户端(超算云地址:https://cloud.paratera. ...
- 【百奥云GS专栏】全基因组选择之模型篇
目录 1. 前言 2. BLUP方法 ABLUP GBLUP ssGBLUP RRBLUP 3. 贝叶斯方法 BayesA BayesB BayesC/Cπ/Dπ Bayesian Lasso 4. ...
- R 语言实战-Part 4 笔记
R 语言实战(第二版) part 4 高级方法 -------------第13章 广义线性模型------------------ #前面分析了线性模型中的回归和方差分析,前提都是假设因变量服从正态 ...
- Metabolomics Workfolw
推荐R语言界的国内大佬于淼写的代谢组学workflow,包含了大部分代谢组学(以及暴露组)的数据分析方法. Meta-Workflow 主要内容包括: Sample collection Pretre ...
- Macbookpro快捷键
Alt键代替 Option 键,用 Windows 标志键代替 Command 键. 但是,使用的时候,Command健类似于Windows的Control键. 1.复制.粘贴.剪切.全选.查找 Co ...
- perl 转置矩阵
这里提供一个转置矩阵的perl脚本,R语言中的t()函数同样也能实现转置 1 use strict; 2 3 open A,"$ARGV[0]"; 4 5 my %ha; 6 my ...
- 35-Remove Element
Remove Element My Submissions QuestionEditorial Solution Total Accepted: 115367 Total Submissions: 3 ...