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

PF4J是一个开源轻量级的Java插件框架,用于系统功能的个性化扩展。本文介绍了PF4J的基本概念、特点、主要类、生命周期、Spring-PF4j集成以及如何在租户场景和处理插件依赖方面进行实践。此外,还探讨了功能模块化的实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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扩展,即扩展点的实现

主要类 

PluginManagerpf4j通过PluginManager向外提供plugin管理能力,如生命周期控制、获取扩展实例
PluginWrapperplugin的包装类
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。

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

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

  • 方式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文件中。

可正常运行插件扩展。

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

功能模块化

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值