写代码我们都知道要抽象,要封装变化,要实现开闭原则,比如对于很多相似的功能,我们可以将通用的功能抽象出来,然后把变化的不同的地方提取出去,比如模版模式、策略模式等都是实现类似的效果
比如对于策略模式,我们通常是定义一个接口,然后有不同的实现,这种是可以的,但是如果通用流程中要扩展的点较多的话,这些不同的实现也需要管理,可以把他们合并到一个单独的包中,再进一步,我们甚至可以将包单独提取出来,支持运行时加载包实现新增功能的支持
JDK对此功能的支持就是 SPI,但是它的限制较多,也不够灵活,比如dubbo就是自己定义了一套SPI的实现,这次我们来看另一个实现,pf4j 提供一套在基本框架中定义扩展点接口,然后通过不同的插件来实现扩展点的功能,来支持对新增开放对修改关闭
这次我们就来学习一下它的使用
使用
比如我们有一套通用的流程,假设是下单流程,不同的业务线等对于下单都有一些特殊点,但是它们的基本流程是相似的,这时候我们就可以先定义好通用的流程,不同的地方预留出扩展点接口,使用 pf4j 的流程如下
- 先定义好扩展点接口(需要定义单独的包,因为基本应用和各个扩展点的包都依赖它)
- 定义单独的插件包,其中实现扩展点接口的功能
- 在应用中编写基本流程和扩展点的发现使用功能
这次我们就参考 pf4j 提供的例子来看一下
1. 定义扩展点接口
pom.xml首先声明依赖
1 2 3 4 5 6 7
| <dependency> <groupId>org.pf4j</groupId> <artifactId>pf4j</artifactId> <version>3.6.0</version> <scope>provided</scope> </dependency>
|
之后即可声明各个扩展点接口
1 2 3 4 5 6 7
|
public interface Notice extends ExtensionPoint { boolean notice(List<Long> userIds); }
|
2. 各个插件实现
pom.xml修改
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target>
<plugin.id>email-plugin</plugin.id> <plugin.version>0.0.1</plugin.version> <plugin.class /> <plugin.provider>user1</plugin.provider> <plugin.dependencies/> </properties>
<dependencies> <dependency> <groupId>org.pf4j</groupId> <artifactId>pf4j</artifactId> <version>3.6.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.zavier.demo</groupId> <artifactId>extension-api</artifactId> <version>1.0.0-SNAPSHOT</version> <scope>provided</scope> </dependency>
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1.1-jre</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.1.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <attach>false</attach> <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> </manifest> <manifestEntries> <Plugin-Id>${plugin.id}</Plugin-Id> <Plugin-Version>${plugin.version}</Plugin-Version> <Plugin-Provider>${plugin.provider}</Plugin-Provider> <Plugin-Class>${plugin.class}</Plugin-Class> <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> </manifestEntries> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
功能实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Extension public class EmailNotice implements Notice {
@Override public boolean notice(List<Long> userIds) { final ArrayList<Object> objects = Lists.newArrayList(); System.out.println("email notice"); return true; } }
|
如果还有其他插件,也是类似的实现
3.插件使用
将之前打包的插件包放到一个路径包下,之后新建一个应用模版,就可以获取插件中的扩展点并使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void main(String[] args) { PluginManager pluginManager = new DefaultPluginManager(Paths.get("/u/plugins")); pluginManager.loadPlugins(); pluginManager.startPlugins();
List<Long> list = new ArrayList<>(); final List<Notice> extensions = pluginManager.getExtensions(Notice.class); extensions.forEach(e -> e.notice(list)); extensions = pluginManager.getExtensions(Notice.class, "email-plugin"); extensions.forEach(e -> e.notice(list));
pluginManager.stopPlugins(); pluginManager.unloadPlugins(); }
|
例子代码地址:https://github.com/zavier/pf4j-demo