什么是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