SPI的全名是Service Provider Interface,可能我们很多开发人员对这项技术不是特别的熟悉,因为这项技术最早是Java提供给厂商和插件开发者的技术支持,java.util.ServiceLoader的文档里有比较详细。简单的总结一下 java spi 的思想:在我们的系统中抽象出了很多的模块,往往有很多不同的实现方案,例如:日志模块、通讯模块、序列化模块等。在面向对象的编程思想中,我们推荐模块之间是基于接口进行编程的,模块之间不以实现类进行硬编码。一旦代码里有了实现类的硬编码,这就违反了插拔原则,如果要替换成另外一种实现,代码就要做非常大的改动。 Java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有些类似于IOC的思想,将装配控制权移到应用之外。在模块化设计中这个机制尤其重要,java spi的具体约定为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。
jdk 原生支持 spi 技术,我们先写一个小的 demo,来看一下原生 spi 是怎么一个样子。
package io.lizardframework.jdk;
public interface Iface {
void sayHello(String name);
}
package io.lizardframework.jdk;
public class RedFace implements Iface {
@Override
public void sayHello(String name) {
System.out.println("red face say hello: " + name);
}
}
package io.lizardframework.jdk;
public class BlueFace implements Iface {
@Override
public void sayHello(String name) {
System.out.println("blue face say hello: " + name);
}
}
package io.lizardframework.jdk;
import java.util.Iterator;
import java.util.ServiceLoader;
public class MainFunc {
public static void main(String[] args) {
ServiceLoader<Iface> ifaceServiceLoader = ServiceLoader.load(Iface.class);
Iterator<Iface> iterator = ifaceServiceLoader.iterator();
while (iterator.hasNext()) {
Iface iface = iterator.next();
iface.sayHello("sage");
}
}
}
我们从刚刚编写的 main 方法入手,进入到ServiceLoader类,观察LazyIterator类中的hasNextService方法,即可看到如下的代码:
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// PREFIX即读取描述文件的路径:"META-INF/services/"
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
从上面的实例代码中,我们可以看到 jdk 原生的 spi 实现虽然使用方便,但是无法提前预知要创建的实例化对象,必须全部遍历创建对象才可以。这样会导致无用的实现也会被实例化,造成资源的浪费和冲突。同时无法做到单例初始化,需要自己实现缓存才可以,编码不灵活。
Lizard-SPI 是一个基于 Java Spi 和 Dubbo Spi 思想基础上的 SPI 支持框架,让使用方快速灵活的扩展应用程序。
io.lizardframework.spi
|
| -- common # 工具包
| -- ExtensionClass # 扩展接口实现类包装
| -- ExtensionFactory # 扩展点加载器工厂
| -- ExtensionLoader # 扩展点加载器
| -- Extensions # 扩展SPI实现注解
| -- SPI # 扩展点接口标示
ExtensionLoader不直接使用构造方法创建,而是通过 ExtensionFactory 工厂的 getExtensionLoader 方法获取到对应类型的扩展点加载器。ExtensionLoader有两个比较重要的方法:
我们首先来看下ExtensionFactory获取ExtensionLoader的过程:
package io.lizardframework.spi;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ExtensionFactory {
private ExtensionFactory() {
}
private static final Map<Class, ExtensionLoader> EXTENSION_LOADER_MAP = new ConcurrentHashMap<>();
/**
* 获取指定接口的扩展点加载器
*
* @param clazz
* @param
* @return
*/
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> clazz) {
ExtensionLoader loader = EXTENSION_LOADER_MAP.get(clazz);
if (loader == null) {
synchronized (ExtensionFactory.class) {
loader = EXTENSION_LOADER_MAP.get(clazz);
if (loader == null) {
EXTENSION_LOADER_MAP.putIfAbsent(clazz, new ExtensionLoader(clazz));
loader = EXTENSION_LOADER_MAP.get(clazz);
}
}
}
return loader;
}
}
我们可以看到,每一个扩展点接口都有一个对应的ExtensionLoader实例。再来看下ExtensionLoader类实例化的流程:
public ExtensionLoader(Class<T> type) {
this.type = type;
// 判断是否有SPI注解
this.isExtensionAnnontation(this.type);
// 装载该接口的SPI扩展实现和 name 的缓存
this.loadExtensionClasses();
}
这里的核心流程是 loadExtensionClasses() 方法,规定了扩展点描述文件的装载路径和装配方式。默认读取的路径是 META-INF/lizard/services/ 和 META-INF/lizard/internal/
/**
* 装载该接口的SPI扩展实现和 name 的缓存
* 使用synchronized同步,避免多次装载
* @return
*/
private synchronized void loadExtensionClasses() {
this.loadFiles(SERVICR_DIRECTORY);
this.loadFiles(LIZARD_INTERNAL_DIRECTORY);
}
/**
* 装载SPI描述文件
*
* @param dir
*/
private void loadFiles(String dir) {
// 获取类型扩展点路径
String fileName = dir + this.type.getName();
try {
// 获取ClassLoader,并装载配置文件
ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
Enumeration<URL> urls = classLoader != null ? classLoader.getResources(fileName) : ClassLoader.getSystemResources(fileName);
if (urls != null) {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
String line = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), DEFAULT_CHARSET))) {
while ((line = reader.readLine()) != null) {
line = line.trim();
// # 开头为注释,直接忽略
if (line.startsWith("#")) continue;
// name=实现接口
int equalIndex = line.indexOf("=");
String name = null;
String clazzName = null;
// 获取 name 和实现类名
if (equalIndex > 0) {
name = line.substring(0, equalIndex).trim();
clazzName = line.substring(equalIndex + 1).trim();
}
// 如果实现类不为空
if (clazzName != null && clazzName.length() > 0) {
// 第二个参数 true 初始化,执行 static 块
Class<?> clazz = Class.forName(clazzName, true, classLoader);
// 如果当前配置的实现类不是 type 的实现,抛出异常
if (!this.type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
this.type + ", class line: " + clazz.getName() + "), class " + clazz.getName() +
" is not subtype of interface");
}
// 判断实现类是否标有Extensions注解
Extensions extensions = clazz.getAnnotation(Extensions.class);
if (extensions == null) {
throw new IllegalStateException("Error when load extension class(interface: " +
this.type + ", class line: " + clazz.getName() + "), class " + clazz.getName() +
" without @" + Extensions.class + " Annontation");
}
// 通过 name 获取是否已经装载过同名的扩展配置类
ExtensionClass<T> alreadyClazz = CACHE_EXTENSION_CLAZZ.get(name);
if (alreadyClazz == null) {
CACHE_EXTENSION_CLAZZ.put(name, new ExtensionClass(clazz, name, extensions));
} else {
throw new IllegalStateException("Duplicate extension " + this.type.getName() + " name: " + name
+ " on: " + alreadyClazz.getClazz() + " and: " + clazz.getName() + " in: " + fileName);
}
}
}
} catch (Exception cause) {
// 将异常写入 map,当获取 clazz 为空时,就将异常输出
exceptions.put(line, cause);
}
}
}
} catch (IOException e) {
//todo logger
}
}
扩展点的每一个实现类都被包装为ExtensionClass对象,getInstance方法通过@Extensions注解,判断实例化对象是否为单例:
public class ExtensionClass<T> {
/**
* 实现类的Extensions注解
*/
private Extensions extensions;
/**
* 获取实例对象
*
* @param argTypes
* @param args
* @return
*/
public T getInstance(Class[] argTypes, Object[] args) {
if (clazz != null) {
if (extensions.singleton()) {
// 创建单例对象
if (instance == null) {
synchronized (this) {
if (instance == null) {
instance = ClassUtils.newInstanceWithArgs(clazz, argTypes, args);
}
}
}
return instance;
} else {
// 直接创建对象返回
return ClassUtils.newInstanceWithArgs(clazz, argTypes, args);
}
}
throw new IllegalStateException("class of ExtensionClass is null");
}
}
最后,调用ExtensionLoader的getExtension方法,获取对应接口的扩展实现。如果调用无参方法,默认的 name 为 default,同时调用无参构造函数创建单例实现类。这里我们就不再具体赘述。
Lizard-SPI的源码和单元测试实例已经全部上传到 github 中,欢迎大家试用并提出改正意见,谢谢!
github:https://github.com/lizard-framework/lizard-spi