插件框架PF4J-从理论到实践

PF4J:Plugin Framework for Java

目录

是什么?

不是什么?

特点

组件

主要类

流程概述

spring-pf4j

思考

功能模块化


我对pf4j的封装和使用demo

GitHub - chlInGithub/pf4jDemo: pf4j demo

是什么?

开源轻量级的插件框架。通过插件形式对系统功能进行个性化扩展。插件需要实现扩展点,扩展点由系统进行定义。

不是什么?

功能模块化加载框架。虽然介绍中描述pf4j可以将庞大的系统转化为模块系统,但依据我的实践来看,仅仅依赖pf4j只能动态加载扩展,无法动态加载完整的功能模块。

特点

  • 开源轻量级的插件框架
  • 简单的标记扩展点,使用interface ExtensionPoint即可将接口和抽象类定义为扩展点
  • 简单的标记扩展,使用@Extension即可定义一个扩展

组件

插件  等同于 由 扩展点、扩展、生命周期行为 构成的一个集合。 

Plugin 所有插件的基类。每一个插件均由单独的classloader进行加载,避免冲突。
PluginManager 对插件进行切面化管理,如loading, starting, stopping。已提供3中实现,JarPluginManager, ZipPluginManager, DefaultPluginManager(jar_zip)。也可自行实现个性化pluginManager,需要实现AbstractPluginManager。
PluginLoader 加载插件需要的所有信息,如class
ExtensionPoint 扩展点
Extension 扩展,即扩展点的实现

主要类 

PluginManager pf4j通过PluginManager向外提供plugin管理能力,如生命周期控制、获取扩展实例
PluginWrapper plugin的包装类
ManifestPluginDescriptorFinder 从(支持jar\zip\目录)manifest文件读取插件信息,如Plugin-Id(用于避免重复load)、Plugin-Version、Plugin-Class
PluginLoader

用于load plugin需要的所有信息,每个pluginPath对应一个pluginLoader,为每个plugin提供独立的classloader。

如JarPluginLoader

PluginClassLoader 自定义的classLoader,修改了loadClass的逻辑。每个plugin对应一个PluginClassLoader实例
PluginState 插件生命周期期间的各种状态
DependencyResolver 插件间依赖关系分解器。一系列插件之间构建依赖图,唯一入口resolve可以返回依赖分解结果,如插件之间是否存在循环依赖、能否找到依赖的插件、插件依赖版本是否正确等。
LegacyExtensionFinder 读取plugin包中META-INF/extensions.idx文件,获取扩展信息和实例。

生命周期概述

加载插件过程 MF文件中插件描述-->插件独立classLoader-->插件间依赖解析-->插件已解析状态
开始插件过程 加载Plugin实现并生成实例-->调用Plugin.start()-->插件已开始状态
获取某个扩展点实现类的实例
停止插件 插件状态变为停止
卸载插件 从集合中清除,关闭classLoader

spring-pf4j

将扩展注册为spring ioc bean

思考

如何支持租户场景

pf4j非线程安全,允许不同租户维护各自插件的场景,需要在pf4j基础上再包裹一层,一方面增加线程安全,一方面维护租户与插件、扩展的关系。

需要以一种路径规范存放插件jar,如下图:

如何支持插件依赖的jar

  • 方式1:系统列举出支持的jar集合,插件依赖其中的jar。在demo中已体现该场景,插件正常运行。

demo:plugin_depend_app_jars

如图为插件的pom依赖,插件jar中不包含依赖的任何jars。

插件框架PF4J-从理论到实践_第1张图片

插件jar放到上图的租户路径下,在系统中运行Pf4jTest.main,插件扩展运行正常。因为系统的classpath中包含common-lang jar,所以classloader可以找到common-lang中class。

扩展运行结果如下,可以清晰看出,两个插件的classloader是独立的。

插件框架PF4J-从理论到实践_第2张图片

  • 方式2:插件jar内部包含依赖的jars
    • 方式2.1:jdk的classloader不支持嵌套jar,需参考spring LaunchedURLClassLoader或JarClassLoader(http://www.jdotsoft.com/JarClassLoader.php)。在不改变pf4j源码情况下,无法实现该方案,因为pf4j使用URLClassLoader。
    • 方式2.2:解压插件jar-->pf4j加载插件jar-->修改生成的classloader的扫描范围,添加依赖jars路径-->启动插件jar

demo:pf4jtest_plugin_self_jars。

注意2点:

  • 需要规范jar包的命名规则:包括插件jar包名规则、包含依赖jars的最终jar包名规则。
  • 使用maven-assembly-plugin进行打包。

插件中依赖commons-math3,而系统没有依赖该jar。

系统加载插件时按照"方式2.2"的步骤进行处理,如plugin.jar解压到plugin文件中。

插件框架PF4J-从理论到实践_第3张图片

可正常运行插件扩展。

插件框架PF4J-从理论到实践_第4张图片

  • 方式3:系统根据插件jar的(maven)依赖去下载依赖的jar

功能模块化

基本能力:系统功能进行模块划分,模块可动态加载和卸载,模块独立且隔离,模块间资源不产生冲突。
设计思路:独立的classloader(修改加载class的顺序,自有范围-->双亲委派)、springcontext、生命周期管理
 

你可能感兴趣的:(技术思考总结,pf4j,插件,扩展)