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
    List 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。

使用示例

引入依赖


    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 List 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 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

相关