PF4J是一个Java轻量级的插件框架,可以实现动态加载,执行,卸载外部插件(支持jar
以及zip
),具体可以看官网介绍 。
本文例子基于Github地址:https://github.com/pf4j/pf4j
1 2 3 4 5 <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
:
1 2 3 4 5 6 7 8 package plugin.api;import org.pf4j.ExtensionPoint;public interface Greeting extends ExtensionPoint { String getGreeting () ; }
实现插件(plugins) 插件需要实现plugin-api
定义的接口,并且使用@Extension
标记:
1 2 3 4 5 6 7 8 9 10 11 12 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
):
1 2 3 4 5 6 7 8 9 10 11 12 13 <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) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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) { 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" ); } }
运行输出:
其他 插件周期 如果对插件生命周期(如加载,执行,停止等)有兴趣的话,可以实现插件类继承Plugin
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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
写入插件信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <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 >
打包后运行输出:
1 2 3 WelcomePlugin.start() >>> Welcome WelcomePlugin.stop()
日志 如果对运行流程感兴趣,或者调试,可以将日志级别设为debug
log4j.properties
示例:
1 2 3 4 5 6 7 8 9 10 11 12 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