SPI与服务发现机制
SPI
SPI(Service Provider Interface)即服务提供者接口,是用来对服务的提供者进行规范的接口。API与SPI都是对外提供的具有某些功能的接口,提供API的目的是让外界能够使用这些功能,提供SPI的目的是让外界去实现这些功能。对于不同的使用者,相同的接口可能体现为不同的意义(API或者SPI),比如在WEB编程中,会创建一个Service接口,还会创建其实现类ServiceImpl类,此时Service接口体现为一个SPI,当其他地方需要使用该Service接口时,这时Service接口体现为一个API。
服务发现机制
通过服务发现机制能够将SPI与其实现进行分离以达到解耦的目的,从而大幅度提升程序的可扩展性。服务发现机制在Java中有比较广泛的应用,比如JDBC模块。JDBC模块提供的Driver接口就是一个SPI,许多厂商都实现了该SPI(如com.mysql.jdbc.Driver),JDBC模块通过服务发现机制来获取到相应的Driver实现类。在规划某个实际项目的模块框架时,也可以考虑使用服务发现机制来提高模块的可扩展性。
Java提供了ServiceLoader类来帮助构建基于服务发现的模块(ServiceLoader类只是进行服务发现的工具,具体实现还需要根据模块功能进行规划)。ServiceLoader.load(Class<S> service)方法可以返回/WEB-INF/services目录下,以S接口的全类名为文件名的文件中配置的,所有S接口的实现类。
// 服务发现机制
public final class ServiceLoader<S> implements Iterable<S> {
// 加载所有ClassPath(包括jar包)下的/WEB-INF/services目录下,名为接口S类全名的所有文件中的,配置的所有S接口的实现类
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader); // 使用指定的加载器进行加载
public static <S> ServiceLoader<S> load(Class<S> service); // 使用加载调用者的类加载器进行加载
public static <S> ServiceLoader<S> loadInstalled(Class<S> service); // 使用扩展类加载器进行加载
public Iterator<S> iterator(); // 对加载的所有实现类进行迭代的迭代器
public void reload(); // 重新加载
}
假设需要将Java对象与一种自定义的XO格式字符串(如同JSON格式)进行相互转换,需要先提供框架相关的XOTool接口与XoToolManager类。各个提供方根据框架提供XOTool的实现类,最后提供方需要将实现类封装成jar包并在其/WEB-INFO/services/com.java.test.common.XOTool文件中添加一行com.baidu.xo.BaiDuXOTool,具体示例代码如下:
package com.java.test.common;
import java.util.Iterator;
import java.util.ServiceLoader;
// XO转换服务的接口,对服务使用方来说是API,对服务提供方来说是SPI
interface XOTool {
<T> T parser(String s, Class<T> clazz);
String toXOString(Object object);
}
// XO服务的主要框架,通过服务发现机制来获取XOTool
class XoToolManager {
private static ServiceLoader<XOTool> xoToolServiceLoader;
static {
xoToolServiceLoader = ServiceLoader.load(XOTool.class);
}
public static XOTool getXoTool(String xoToolName) throws Exception {
Iterator<XOTool> xoToolIterator = xoToolServiceLoader.iterator();
while (xoToolIterator.hasNext()) {
XOTool xoTool = xoToolIterator.next();
if (xoTool.getClass().getName().equals(xoToolName)) {
return xoTool;
}
}
throw new Exception("没有符合要求的XoTool实现类");
}
}
public class SPITest {
public static void main(String[] var) throws Exception {
XOTool xoTool = XoToolManager.getXoTool("com.wanglang.MyXOTool");
Object object = xoTool.parser("我是某个对象XO格式的字符串", Object.class);
}
}