• pf4j 试用


    pf4j 试用上还是比较灵活的,并没有太多的配置,而且比较灵活,支持类隔离

    参考项目

    • 项目结构
    ├── README.md
    ├── bootstrap  // 启动入口,使用了assembly 进行打包,当然对图spring 项目也是可以的
    ├── pom.xml
    └── src
    ├── main
    ├──assembly
    ├── java
    └── resources
    └── test
    └── java
    ├── loginpluginb  // 插件1
    ├── pom.xml
    └── src
    ├── main
    ├── java
    └── resources
    └── test
    └── java
    ├── loginpluginc // 插件2
    ├── pom.xml
    └── src
    ├── main
    ├── java
    └── resources
    └── test
    └── java
    ├── pom.xml
    ├── service-contract // 插件服务契约
    ├── pom.xml
    └── src
    ├── main
    ├── java
    └── resources
    └── test
    └── java
    └── src
        ├── main
        ├── java
        └── resources
        └── test
            └── java
    • 代码说明
      service-contract 定义实现契约注意需要继承ExtensionPoint (所以也需要添加pf4j依赖,推荐使用provide模式)
     
    package com.dalong;
     
    import org.pf4j.ExtensionPoint;
     
    /**
     * @author dalong
     * userlogin service contract
     */
    public interface UserLogin extends  ExtensionPoint {
        /**
         * userlogin service contract
         * @param name name
         * @param password password
         * @return token
         */
        String token(String name,String password);
    }

    插件实现(继承Plugin)进行扩展,添加注解@Extension
    实现Plugin的目的是进行生命周期的控制

     
    package com.dalong;
     
    import org.pf4j.Extension;
    import org.pf4j.Plugin;
    import org.pf4j.PluginWrapper;
     
    /**
     * login plugin c
     */
    public class MyLoginPluginC extends Plugin {
        public MyLoginPluginC(PluginWrapper wrapper) {
            super(wrapper);
        }
     
        @Override
        public void delete() {
            super.delete();
            System.out.println("pluginc  delete");
        }
     
        @Override
        public void stop() {
            super.stop();
            System.out.println("pluginc  stop");
     
        }
     
        @Override
        public void start() {
            super.start();
            System.out.println("pluginc  start");
     
        }
        @Extension
        public  static  class MyLoginC implements  UserLogin {
     
            @Override
            public String token(String name, String password) {
                return String.format("%s-%s-plugin c",name,password);
            }
        }
    }

    插件打包说明
    默认插件的加载包含了classpath 以及serviceloader(spi,但是默认没开启)可以基于jar 的manifest以及plugin.properties
    基于jar 模式比较好,而且比较标准

     
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>pf4j-learning</artifactId>
            <groupId>com.dalong</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
     
        <artifactId>loginpluginc</artifactId>
     
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <plugin.id>loginpluginc</plugin.id>
            <plugin.class>com.dalong.MyLoginPluginC</plugin.class>
            <plugin.version>0.0.1</plugin.version>
            <plugin.provider>dalong</plugin.provider>
            <plugin.dependencies />
        </properties>
     
        <dependencies>
            <dependency>
                <groupId>com.dalong</groupId>
                <artifactId>service-contract</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.pf4j</groupId>
                <artifactId>pf4j</artifactId>
                <version>3.6.0</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    // manifest 维护
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <configuration>
                        <archive>
                            <manifestEntries>
                                <Plugin-Id>${plugin.id}</Plugin-Id>
                                <Plugin-Class>${plugin.class}</Plugin-Class>
                                <Plugin-Version>${plugin.version}</Plugin-Version>
                                <Plugin-Provider>${plugin.provider}</Plugin-Provider>
                                <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    bootstap 入口,比较简单,包含了支持serviceloader 的以及自定义classpath的
    servieloader模式的

     
    public class MyDefaultLogin implements  UserLogin{
        @Override
        public String token(String name, String password) {
            return String.format("%s-%s-default ",name,password);
        }
    }
     

    自定义的

    package com.dalong;
     
    import org.pf4j.Extension;
     
    @Extension
    public class Pf4JLogin implements  UserLogin{
        @Override
        public String token(String name, String password) {
            return String.format("%s-%s-Pf4JLogin ",name,password);
        }
    }
     

    入口打包(方法很多,可以使用shared 以及maven-assembly-plugin)
    maven pom.xml


     
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>pf4j-learning</artifactId>
            <groupId>com.dalong</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
     
        <artifactId>bootstrap</artifactId>
     
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <log4j.version>2.17.1</log4j.version>
            <main.class>com.dalong.Application</main.class>
        </properties>
     
        <dependencies>
            <dependency>
                <groupId>org.pf4j</groupId>
                <artifactId>pf4j</artifactId>
                <version>3.6.0</version>
            </dependency>
            <dependency>
                <groupId>com.dalong</groupId>
                <artifactId>service-contract</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-slf4j-impl</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>2.3</version>
                    <configuration>
                        <descriptors>
                            <descriptor>src/main/assembly/assembly.xml</descriptor>
                        </descriptors>
                        <appendAssemblyId>false</appendAssemblyId>
                    </configuration>
                    <executions>
                        <execution>
                            <id>make-assembly</id>
                            <phase>package</phase>
                            <goals>
                                <goal>attached</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.5.1</version>
                    <configuration>
                        <annotationProcessors>
                            <annotationProcessor>org.pf4j.processor.ExtensionAnnotationProcessor</annotationProcessor>
                        </annotationProcessors>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId> // 配置一些manifest
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                                <classpathPrefix>lib/</classpathPrefix>
                                <mainClass>${main.class}</mainClass>
                            </manifest>
                        </archive>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    assembly.xm

    <assembly>
        <id>app</id>
        <formats>
            <format>dir</format>
            <format>zip</format>
        </formats>
        <includeBaseDirectory>false</includeBaseDirectory>
        <dependencySets>
            <dependencySet>
                <useProjectArtifact>false</useProjectArtifact>
                <outputDirectory>lib</outputDirectory>
                <includes>
                    <include>*:jar:*</include>
                </includes>
            </dependencySet>
        </dependencySets>
        <fileSets>
            <fileSet>
                <directory>${project.build.directory}</directory>
                <outputDirectory></outputDirectory>
                <includes>
                    <include>*.jar</include>
                </includes>
                <excludes>
                    <exclude>*-javadoc.jar</exclude>
                    <exclude>*-sources.jar</exclude>
                </excludes>
            </fileSet>
        </fileSets>
    </assembly>

    入口代码

    package com.dalong;
     
    import org.pf4j.*;
     
    import java.util.List;
    import java.util.function.Consumer;
     
    public class Application {
        public static void main(String[] args) {
            PluginManager pluginManager = new DefaultPluginManager(){
                @Override
                protected ExtensionFinder createExtensionFinder() {
                    DefaultExtensionFinder extensionFinder= (DefaultExtensionFinder) super.createExtensionFinder();
                    extensionFinder.addServiceProviderExtensionFinder();// 开启servieloader 模式,
                    return extensionFinder;
                }
            };
            pluginManager.loadPlugins();
            pluginManager.startPlugins();
            pluginManager.getPlugins().forEach(new Consumer<PluginWrapper>() {
                @Override
                public void accept(PluginWrapper pluginWrapper) {
                    System.out.println("load plugin:"+pluginWrapper.getPluginId()+pluginWrapper.getPluginState());
                }
            });
            List<UserLogin> userLoginList=  pluginManager.getExtensions(UserLogin.class);
            userLoginList.forEach(new Consumer<UserLogin>() {
                @Override
                public void accept(UserLogin userLogin) {
                    System.out.println(userLogin.token("name","dalong"));
                }
            });
        }
    }

    构建&启动

    • 构建
    mvn clean package
    • 使用
      pf4j 对于插件加载有自己的流程,默认是运行目录的plugins 下,对于pf4j可以是jar 也可以是zip 文件(后边会介绍)


    运行效果
    截取部分

    问题

    • 关于插件目录
      默认pf4j是当前运行目录的plugins下查找的,我们可以在运行的时候指定取值为System.getProperty("pf4j.pluginsDir", "plugins")
      启东时配置java -Dpf4j.pluginsDir=demoapp -jar bootstrap-1.0-SNAPSHOT.jar
    • 插件包元数据
      推荐基于jar 文件定义,可以通过jar 插件
    • 线程安全问题
      AbstractPluginManager 以及 DefaultPluginManager 都不是线程安全的,所以加载的时候需要自己包装线程安全
    • 几个扩展
      官方还提供了几个很不错的扩展spring,update。。。具体可以参考github
    • 默认ExtensionFactory
      默认是基于Class.newInstance java9 以及废弃了,而且如果有构造函数的就不方便了,可以使用Constructor.newInstance 或者自己开发
      因为实际中我们很多时候是需要传递参数的,比较推荐的是在服务契约中定义一个context,我们基于context进行服务的创建
    • 插件的开启以及禁用
      pf4j 提供了基于文本的插件配置,enabled.txt 以及disabled.txt我们可以开启以及禁用插件,文件内容就是插件id
    • 插件的依赖
      扩展可以包含依赖,可以基于注解添加,但是注意需要asm包,同时注意插件依赖,必须配置为可选的
    • 没有发现插件
      插件基于了java annotation processing,需要包含一个extensions.idx文件,可以在compile的时候指定java annotation processing
      也可以通过日志查看
     
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
            <annotationProcessors>
                <annotationProcessor>org.pf4j.processor.ExtensionAnnotationProcessor</annotationProcessor>
            </annotationProcessors>
        </configuration>
    </plugin>
    • 测试
      pf4j 提供了测试包,可以用来进行方便的测试,目前有PluginJar,PluginZip 以及ClassDataProvider
    • Fat jar 的一个问题
      对于fat jar 可能会出现插件加载不成功的问题,比如我们的一个插件需要依赖其他jar,一般我们可能会通过shared 解决
      注意对于服务契约使用scope provider 模式,很重要
      参考pom 配置
     
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>pf4j-learning</artifactId>
            <groupId>com.dalong</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
     
        <artifactId>loginpluginc</artifactId>
     
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <plugin.id>loginpluginc</plugin.id>
            <plugin.class>com.dalong.MyLoginPluginC</plugin.class>
            <plugin.version>0.0.1</plugin.version>
            <plugin.provider>dalong</plugin.provider>
            <plugin.dependencies />
        </properties>
     
        <dependencies>
            <dependency>
                <groupId>com.dalong</groupId>
                <artifactId>service-contract</artifactId>
                <version>1.0-SNAPSHOT</version>
                <scope>provided</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.pf4j</groupId>
                <artifactId>pf4j</artifactId>
                <version>3.6.0</version>
                <scope>provided</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.hashids</groupId>
                <artifactId>hashids</artifactId>
                <version>1.0.3</version>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <configuration>
                        <archive>
                            <manifestEntries>
                                <Plugin-Id>${plugin.id}</Plugin-Id>
                                <Plugin-Class>${plugin.class}</Plugin-Class>
                                <Plugin-Version>${plugin.version}</Plugin-Version>
                                <Plugin-Provider>${plugin.provider}</Plugin-Provider>
                                <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.3</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>

    参考资料

    https://pf4j.org/doc/getting-started.html
    https://github.com/rongfengliang/pf4j-learning
    https://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html
    https://asm.ow2.io/
    https://pf4j.org/doc/plugins.html#optional-plugin-dependencies
    https://pf4j.org/doc/testing.html

  • 相关阅读:
    [抄书]The Pipes and Filters pattern
    [抄书]The Layers pattern
    OpenGL Step by Step (1)
    [HOOPS]二维点向三维空间投影
    心仪已久的工具:BoundsChecker v7.2
    [HOOPS]用HC_Show_...获取正确的点的坐标位置
    小试zlib
    XML (2) Document Type Definitions (DTD)
    UML (1) 设计模式及作业附图
    XML (1) 什么是XML
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/15898281.html
Copyright © 2020-2023  润新知