pf4j及pf4j-spring
什么是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 。
导入依赖
org.pf4j pf4j 3.0.1
自定义扩展接口,集成 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 Listgreetings = 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。
使用示例
引入依赖
org.pf4j pf4j-spring ${pf4j-spring.version}
这里的版本号,你可以去maven仓库里拿最新的,也可以在pom文件里添加下面的代码取最新的。
sonatype-nexus-snapshots https://oss.sonatype.org/content/repositories/snapshots false true
建议使用 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 Listgreetings; 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 Listgreetings = 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