PF4J:轻量级 Java 插件框架
插件是第三方扩展系统应用功能的一种方式。
插件实现由应用程序或其他插件声明的扩展点。
插件还可以定义扩展点。
使用 PF4J,您可以轻松地将单体 Java 应用程序转换为模块化应用程序。
当前构建 PF4J 所需的最低 Java 版本应该是 9,但运行时 Java 版本可以是 8,因为该工件是一个多版本 jar。
组件:
Plugin
: 所有插件类型的基类。每个插件都加载到单独的类加载器中以避免冲突PluginManager
: 用于插件管理的各个方面(loading, starting, stopping)。您可以使用内置实现作为DefaultPluginManager
,也可以从AbstractPluginManager
开始实现自定义插件管理器(仅实现工厂方法)PluginLoader
: 加载插件所需的所有信息(classes)ExtensionPoint
: 应用程序中可以调用自定义代码的点。它是一个java接口标记。任何java接口或抽象类都可以被标记为扩展点(实现ExtensionPoint
接口)。Extension
: 扩展点的实现。这是一个类上的java注解。- PluginStateListener:
DefaultExtensionFinder
在所有扩展索引文件 META-INF/extensions.idx
中查找扩展。 PF4J 使用 Java 注解处理在编译时处理所有用 @Extension 注解的类并生成扩展索引文件。
插件存储在一个文件夹中。您可以在 DefaultPluginManager 的构造函数中指定插件文件夹。如果未指定插件文件夹,则由 System.getProperty("pf4j.pluginsDir", "plugins")
返回位置。
注意:“pf4j.pluginsDir”属性附带逗号分隔的目录列表支持(支持多个插件根目录)。
未生成 extensions.idx ,需要配置一下Maven
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<annotationProcessors>
<annotationProcessor>org.pf4j.processor.ExtensionAnnotationProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
打包:支持 zip、jar(推荐) 格式
如果您不需要在运行时在应用程序中加载/卸载 Java 代码的某些部分,则根本没有必要使用插件。您也可以仅使用扩展并将编译的类放入应用程序类路径(所谓的系统扩展)。
编写原数据
- META-INF/MANIFEST.MF 或者 maven-jar-plugin、maven-assembly-plugin(plugin.properties)
plugin.class=org.pf4j.demo.welcome.WelcomePlugin
plugin.id=welcome-plugin
plugin.version=0.0.1
plugin.requires=1.0.0
#依赖插件版本
plugin.dependencies=x, y, z
plugin.description=My example plugin
plugin.provider=Decebal Suiu
plugin.license=Apache License 2.0
流程
demo:https://github.com/pf4j/pf4j/tree/master/demo
- 定义可扩展接口(Plugin-Api)
- 实现插件(Plugins)
- 插件打包(Package)
- 加载执行插件(Plugin-App)
生命周期
主要插件状态是:
CREATED
DISABLED
RESOLVED
STARTED
STOPPED
DefaultPluginManager
包含以下逻辑:
- 所有插件均已解析并加载
DISABLED
插件不会由startPlugins()
中的 pf4j 自动STARTED
,但您可以通过调用startPlugin(pluginId)
手动启动(并因此启用)已禁用的插件而不是enablePlugin(pluginId)
+startPlugin(pluginId)
- 只有
STARTED
插件可以提供扩展。任何其他状态都不应该被视为准备好为正在运行的系统提供扩展。
DISABLED
插件和 STARTED
插件之间的区别是:
STARTED
插件已执行Plugin.start()
,DISABLED
插件尚未执行STARTED
插件可能会提供扩展实例,DISABLED
插件可能不会
自定义插件管理器
要创建自定义插件管理器,您必须选择以下选项之一:
- 实现
PluginManager
接口(从头开始创建插件管理器) - 修改内置实现的某些方面/行为(
DefaultPluginManager
) - 扩展
AbstractPluginManager
类
开发模式
- DEVELOPMENT:模式是插件创建的标准工作流程:为每个插件创建一个新的 Maven 模块,编码插件(声明新的扩展点和/或添加新的扩展),将插件打包在 zip 文件中,部署 zip文件到插件文件夹。这些操作非常耗时,因此我引入了DEVELOPMENT运行时模式。
- DEPLOYMENT:模式的主要优点是他/她不会被迫打包和部署插件。在开发模式下,您可以以简单快速的方式开发插件。
Extension
- ordinal:扩展加载顺序
- points:扩展点类列表
- plugins:依赖插件列表
public @interface Extension {
/**
* 扩展的顺序。
* 序号用于对扩展名进行排序。
*
* @return 扩展的顺序
*/
int ordinal() default 0;
/**
* 由此扩展实现的扩展点数组。
* 此显式配置将覆盖 org.pf4j.processor.ExtensionAnnotationProcessor 中扩展点的自动检测。
*
* 如果扩展直接派生自扩展点,则不需要此属性。但在某些 更复杂的方案 https://github.com/pf4j/pf4j/issues/264 中,显式设置扩展的扩展点可能很有用。
* @return 由此扩展实现的扩展点类
*/
Class<? extends ExtensionPoint>[] points() default {};
/**
* 插件 ID 数组,必须可用才能加载此扩展。
* AbstractExtensionFinder 如果这些插件在运行时不可用/ 启动,则不会加载此扩展。
* 注意:此功能要求可选的 ASM 库 在应用程序类路径上可用,并且必须通过 AbstractExtensionFinder. setCheckForExtensionDependencies(boolean)显式启用。
*
* @return 插件 ID,必须可用才能加载此扩展
*/
String[] plugins() default {};
}
实例化:ExtensionFactory
当调用 plugin.getExtensions(MyExtensionPoint.class)
时,将根据需要创建扩展实例。默认情况下,如果您调用 plugin.getExtensions(MyExtensionPoint.class)
两次:
然后对于每个调用,都会创建一个新的扩展实例。
如果要返回相同的扩展实例(单例),则需要使用SingletonExtensionFactory:
常见问题
- No Extensions Found
如果文件 extensions.idx
不存在,则注释处理步骤可能有问题(在 IDE 或 Maven 脚本中启用注释处理)。
如果文件 extensions.idx
存在,并且它不为空,那么肯定存在类加载器问题(在两个不同的类加载器中具有相同的扩展点),在这种情况下,您必须删除一些库(可能是 API jar )来自插件。
如果问题仍然存在,或者您想查找与扩展发现过程相关的更多信息(例如,每个插件加载哪些接口/类,哪些类不被识别为扩展点的扩展),那么您必须放置 TRACE
级别 PluginClassLoader
和 AbstractExtensionFinder
的记录器(请参阅 log4j2.properties 文件进行演示)。
评论