1.SPI 介绍
在进行应用程序开发的时候,经常有这样一类需求,在不修改源代码的情况下,动态的为我们的程序提供一系列特性,比如我们可以为每一个方法在执行前,执行后动态的运算它的执行时间;或者收集jvm、内存、cpu的运行指标等等,像这些灵活扩展的功能该如何实现呢?在springboot的框架里有aop技术,也就是面向切面编程来构建我们的应用插件,这是其中的一种方式
在java自带的领域就有着完整类似的实现,可以通过语言方面的机制,帮助我们动态的进行能力上的扩展,这就是SPI机制。
对于SPI来说,它不是一个复杂的技术,它是一套开发的规范,只要符合了这个规范,就可以动态的为程序进行插件的扩展和功能的增强了。
2.案例
我希望在⾃⼰的系统中可以增加灵活的插件机制,并且不需要修改源代码,只需要通过往程序中增加一个一个编译好的插件jar包,就可以为应⽤程序灵活扩展新的功能。
2.1.
定义SPI接口
import java.util.Map; // SPI 接口,所有的动态扩展都是基于这个接口来的 public interface Plugin { public boolean install(Map context); }
编写插件的加载安装工厂:
// 插件工厂,作用,加载目前所有可以被使用的plugin实现类 // 在我们的工程中并没有plugin实现类,但是这并不影响动态加载的过程 public class PluginFactory { public void installPlugins(){ Map context = new LinkedHashMap(); //模拟插件安装或运⾏时所需要的各类应⽤本体信息 //当前IOC容器中的对象集合 context.put("_beans", new ArrayList<>()); context.put("_version", "1.0.0"); //切⾯类 context.put("_aspects", new LinkedHashMap<>()); // 扫描classpath下所有Plugin的实现类 ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class); Iterator<Plugin> iterator = loader.iterator(); while(iterator.hasNext()){ Plugin plugin = iterator.next(); plugin.install(context); } } public static void main(String[] args) { new PluginFactory().installPlugins(); // 这个时候运行是没有任何插件的 } }
增加context上下文的目的:
插件安装过程中,要获取应用程序本身的一些信息,如beans、切面类,或者通过上下文给它开放一些可以给灵活注入的接口,这些我们都给它包含在了Map上下文对象中
2.2.
增加一个额外的spi-app的依赖,目的是实现相同的Plugin接口,在相同的Plugin接口下增加相应的实现
<dependency> <groupId>com.fblinux</groupId> <artifactId>SPI-APP</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
实现接口:在install方法中实现对插件逻辑上的安装
import java.util.Map; public class LogPlugin implements Plugin{ @Override public boolean install(Map context) { System.out.println("LogPlugin已初始化,logger对象已注⼊IOC容器"); return true; } }
此时我们实现了Plugin接口以及install方法,那应该如何当应用程序动态的发现这个插件呢?
这里有一个关键的设计,在resources目录下,要创建一个两级目录META-INF/services⽬录,新建 com.fblinux.spi.Plugin⽂件(和接口全名对应,⽆扩展名)
在SPI-APP目录下,引入SPI-Log-Plugin依赖
<dependency> <groupId>com.fblinux</groupId> <artifactId>SPI-Log-Plugin</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
程序输出结果
3.常用加载plugin途径
maven依赖
应用程序的lib⽬录
应用程序指定的classpath⽬录
转载请注明:西门飞冰的博客 » JAVA—SPI机制