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;
@Extension
public class WelcomeGreeting implements Greeting {
public String getGreeting() {
return "Welcome";
}
}


插件打包(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 class ​​org.pf4j.demo.welcome.WelcomePlugin​​ (optional attribute), with version ​​0.0.1​​ (mandatory attribute) and with dependencies to plugins ​​x, 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);
}
@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​​写入插件信息:

<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


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;
@Extension
public class WelcomeGreeting implements Greeting {
public String getGreeting() {
return "Welcome";
}
}


插件打包(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 class ​​org.pf4j.demo.welcome.WelcomePlugin​​ (optional attribute), with version ​​0.0.1​​ (mandatory attribute) and with dependencies to plugins ​​x, 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);
}
@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​​写入插件信息:

<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