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


免責聲明!

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



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