什么是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的更多相关文章

  1. pf4j实例 插件框架

    实现整个过程需要三个部分,第一就是根接口,第二是插件,第三是应用程序.这是3个java项目. 首先要下载jar包,百度搜索maven repository,然后搜索pf4j,如下图,下载第一个的相应版 ...

  2. PF4J使用

    PF4J是一个Java轻量级的插件框架,可以实现动态加载,执行,卸载外部插件(支持jar以及zip),具体可以看官网:https://pf4j.org/. 本文例子基于Github地址:https:/ ...

  3. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  4. 玩转spring boot——快速开始

    开发环境: IED环境:Eclipse JDK版本:1.8 maven版本:3.3.9 一.创建一个spring boot的mcv web应用程序 打开Eclipse,新建Maven项目 选择quic ...

  5. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  6. [Spring]IoC容器之进击的注解

    先啰嗦两句: 第一次在博客园使用markdown编辑,感觉渲染样式差强人意,还是github的样式比较顺眼. 概述 Spring2.5 引入了注解. 于是,一个问题产生了:使用注解方式注入 JavaB ...

  7. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  8. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  9. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

随机推荐

  1. Docker部署 Mysql .Net6等容器

    Centos8安装Docker 1.更新一下yum [root@VM-24-9-centos ~]# yum -y update 2.安装containerd.io # centos8默认使用podm ...

  2. [hdu6316]Odd shops

    记$m=10$,即商品的种类 记$g(x)=1+\sum_{i=1}^{m}a_{i}x_{i}$,问题即求$f_{n}(x)=g^{n}(x)$非0项数(模2意义下) 注意到$f^{2}(x)\eq ...

  3. ICCV 2021口罩人物身份鉴别全球挑战赛冠军方案分享

    1. 引言 10月11-17日,万众期待的国际计算机视觉大会 ICCV 2021 (International Conference on Computer Vision) 在线上如期举行,受到全球计 ...

  4. 华为9.8笔试题C++

    问题 给出一颗二叉树,每个节点有一个编号和一个值,该值可能为负数,请你找出一个最优节点(除根节点外),使得在该节点将树分成两棵树后(原来的树移除这个节点及其子节点,新的树以该节点为根节点),分成的两棵 ...

  5. vue 事件监听和es6模板语法

    es6模板语法的反引号是通过左上角的飘字符弄出来了,学废了吗?

  6. Codeforces 1368H - Breadboard Capacity(最小割+线段树维护矩阵乘法)

    Easy version:Codeforces 题面传送门 & 洛谷题面传送门 Hard version:Codeforces 题面传送门 & 洛谷题面传送门 首先看到这种从某一种颜色 ...

  7. Python中pymysql基本使用

    Python中pymysql模块通过获取mysql数据库命令行游标执行数据库命令来进行数据库操作 优点:操作数据库语句所见即所得,执行了什么数据库语句都很清楚 缺点:操作繁琐,代码量多 1. pymy ...

  8. goto 的用法

    #include <stdio.h> int main() { printf("go to cpy \n"); goto FLASH_CPY; printf(" ...

  9. Spring Cloud 2021.0.0 正式发布,第一个支持Spring Boot 2.6的版本!

    美国时间12月2日,Spring Cloud 正式发布了第一个支持 Spring Boot 2.6 的版本,版本号为:2021.0.0,codename 为 Jubilee. 在了解具体更新内容之前, ...

  10. python基础实战

    字符串的互相转换 字典的排序 字典的排序可以直接把,key值或者,values拿出来排序 也可以用dict.items拿出所有的key,value的值再加key=lambda x:x[1] 来排序. ...