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

導入依賴

<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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM