SPI 将服务接口与服务实现分离,通过本地注册发现获取具体的实现类,具备可插拔的特点。
SPI 示例
Driver:
public interface Driver {
void connect();
}
public class MySqlDriver implements Driver {
@Override
public void connect() {
System.out.println("mysql connect");
}
}
public class OracleDriver implements Driver {
@Override
public void connect() {
System.out.println("oracle connect");
}
}
在 resouces 目录中创建 META-INF/services 目录,并创建以 Driver 全路径名为文件名的文件,写入具体实现:
com.xmustang.spi.MySqlDriver
Client:
public class Client {
public static void main(String[] args) {
ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
for (Driver driver : drivers) {
driver.connect();
}
}
}
运行结果:
SPI 使用场景
JDBC 就是典型的 SPI 的使用场景。各大厂商(如 Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用 jdbc 时不需要改变代码,直接引入不同的 SPI 接口服务即可。
SPI 原理
public final class ServiceLoader<S> implements Iterable<S> {
// 扫描目录前缀
private static final String PREFIX = "META-INF/services/";
// 被加载的类、接口
private final Class<S> service;
// 用于定位、加载、实例化实现方实现的类的类加载器
private final ClassLoader loader;
// 安全访问器
private final AccessControlContext acc;
// 按照实例化顺序缓存已经实例化的类
private LinkedHashMap<String, S> providers = new LinkedHashMap<>();
// 懒查找迭代器
private java.util.ServiceLoader.LazyIterator lookupIterator;
public Iterator<S> iterator() {
return new Iterator<S>() {
Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
// 私有内部类,提供对所有的 service 的类的加载与实例化,实现 Iterator 接口
private class LazyIterator implements Iterator<S> {
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// 通过相对路径读取 classpath 中 META-INF 目录中文件,也就是读取服务提供者的实现类全限定名
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;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
// 反射加载类
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service, "Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service, "Provider " + cn + " not a subtype");
}
try {
// 实例化
S p = service.cast(c.newInstance());
// 放进缓存
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service, "Provider " + cn + " could not be instantiated", x);
}
throw new Error(); // This cannot happen
}
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() {
return hasNextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() {
return nextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
}
}