什么是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. myeclipse重写快捷键

    shift+alt+s 点击Override/Implments methods

  2. [noi1755]Trie

    定义S对应的数组为$a_{i}=\min_{0\le j<i,S_{j}=S_{i}}i-j$,特别的,若不存在j,令$a_{i}=i$,那么容易发现存在双射关系就意味这两者对应的数组相同 因此 ...

  3. win10的docker配置nginx

    进入容器内部: docker exec -it 2b9676bf24ef /bin/bash配置映射关系:前面是本地的后面是docker的 --privileged=true 是可以多个,百度到的do ...

  4. vue文件引入

    <template> <div class="hello"> <h1>{{ msg }}</h1> <!-- <h1&g ...

  5. pyinstaller进行打包exe文件

    百度直接pip安装,报错 下载离线文件报错. 百度了一下:还真好使 Python生成可执行文件主要有三种方法,利用py2exe,pyInstaller或cx_Freeze. 这里选择pyinstall ...

  6. HTML四种定位-固定定位

    固定定位 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&q ...

  7. Redis线程模型的前世今生

    一.概述 众所周知,Redis是一个高性能的数据存储框架,在高并发的系统设计中,Redis也是一个比较关键的组件,是我们提升系统性能的一大利器.深入去理解Redis高性能的原理显得越发重要,当然Red ...

  8. 如何在 Kubernetes 集群中玩转 Fluid + JuiceFS

    作者简介: 吕冬冬,云知声超算平台架构师, 负责大规模分布式机器学习平台架构设计与功能研发,负责深度学习算法应用的优化与 AI 模型加速.研究领域包括高性能计算.分布式文件存储.分布式缓存等. 朱唯唯 ...

  9. ServerBootstrap的handler()和childHandler()区别

    无论服务端还是客户端都进行了handler的设置,通过添加hanlder,我们可以监听Channel的各种动作以及状态的改变,包括连接,绑定,接收消息等. 区别: 1. handler在初始化时就会执 ...

  10. Swagger2简单使用教程

    Swagger2简单使用教程 1.简介 ​ Swagger是为了解决企业中接口(api)中定义统一标准规范的文档生成工具.很多采用前后端分离的模式,前端只负责调用接口,进行渲染,前端和后端的唯一联系, ...