Loading

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

 

posted @   你比从前快乐;  阅读(3946)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示