PF4J是一個Java輕量級的插件框架,可以實現動態加載,執行,卸載外部插件(支持jar
以及zip
),具體可以看官網:https://pf4j.org/。
本文例子基於Github地址:https://github.com/pf4j/pf4j
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>3.0.1</version>
</dependency>
插件項目會涉及到3個工程:工程結構
- plugin-api:定義可擴展接口
- plugins:插件項目,可以包含多個插件,需要實現
plugin-api
中定義的接口 - plugin-app:主程序,需要依賴
plugin-api
,加載並執行plugins
定義可擴展接口(plugin-api)
簡單定義一個接口,需繼承ExtensionPoint
:
package plugin.api;
import org.pf4j.ExtensionPoint;
public interface Greeting extends ExtensionPoint {
String getGreeting();
}
實現插件(plugins)
插件需要實現plugin-api
定義的接口,並且使用@Extension
標記:
package plugins;
import org.pf4j.Extension;
import plugin.api.Greeting;
插件打包(plugins)
插件打包時,需要往MANIFEST.MF
寫入插件信息,此處使用maven
插件(打包命令為package
):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifestEntries>
<Plugin-Id>welcome-plugin</Plugin-Id>
<Plugin-Version>0.0.1</Plugin-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
根據Github上介紹,MANIFEST.MF
中Plugin-Id
以及Plugin-Version
是必須信息:
In above manifest I described a plugin with id
welcome-plugin
(mandatory attribute), with classorg.pf4j.demo.welcome.WelcomePlugin
(optional attribute), with version0.0.1
(mandatory attribute) and with dependencies to pluginsx, y, z
(optional attribute).
此處定義插件ID為welcome-plugin
,版本為0.0.1
加載執行插件(plugin-app)
package plugin.app;
import java.nio.file.Paths;
import java.util.List;
import org.pf4j.JarPluginManager;
import org.pf4j.PluginManager;
import plugin.api.Greeting;
public class Main {
public static void main(String[] args) {
// jar插件管理器
PluginManager pluginManager = new JarPluginManager();
// 加載指定路徑插件
pluginManager.loadPlugin(Paths.get("plugins-0.0.1-SNAPSHOT.jar"));
// 啟動指定插件(也可以加載所有插件)
pluginManager.startPlugin("welcome-plugin");
// 執行插件
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
for (Greeting greeting : greetings) {
System.out.println(">>> " + greeting.getGreeting());
}
// 停止並卸載指定插件
pluginManager.stopPlugin("welcome-plugin");
pluginManager.unloadPlugin("welcome-plugin");
}
}
運行輸出:
>>> Welcome
其他
插件周期
如果對插件生命周期(如加載,執行,停止等)有興趣的話,可以實現插件類繼承Plugin
:
package plugins;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
public class WelcomePlugin extends Plugin {
public WelcomePlugin(PluginWrapper wrapper) {
super(wrapper);
}
同時往MANIFEST.MF
寫入插件信息:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifestEntries>
<Plugin-Id>welcome-plugin</Plugin-Id>
<Plugin-Version>0.0.1</Plugin-Version>
<!-- 新增 -->
<Plugin-Class>plugins.WelcomePlugin</Plugin-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
打包后運行輸出:
WelcomePlugin.start()
>>> Welcome
WelcomePlugin.stop()
如果對運行流程感興趣,或者調試,可以將日志級別設為debug
日志
log4j.properties
示例:
log4j.rootLogger = debug,stdout,log
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d [%-5p] %l %r ms: %m%n
log4j.appender.log = org.apache.log4j.DailyRollingFileAppender
log4j.appender.log.DatePattern = _yyyy-MM-dd
log4j.appender.log.File = logs/debug.log
log4j.appender.log.Encoding = UTF-8
log4j.appender.log.layout = org.apache.log4j.PatternLayout
log4j.appender.log.layout.ConversionPattern = %d [%-5p] (%c.%t): %m%n