个人理解
SPI为接口的具体实现提供一种发现机制, 比如说: 某权威机构为一类功能提供了一种标准(interface), 但自己不做具体的实现(implement), 其他各组织机构都需要按照这个标准(interface)来完成各自的相应实现(implement), 然后在应用中可以动态的获取这种标准的所有实现, 之后就是操作者可以通过自己的需求进行具体实现的调用了!
纯属个人理解, 如有大神, 请赐教, 感激不尽
简单说: SPI的功能就是**在当前应用环境下发现某接口的所有具体实现 **
如果还不明白, 看完下面简单实用再回来看这几句话
简单实现
java SPI 基本使用
提供的驱动接口
package com.zcz.study.spi;
public interface IDriver {
void run();
}
驱动实现A
package com.zcz.study.spi;
public class ADriver implements IDriver {
public void run() {
System.out.println("驱动 A");
}
}
驱动实现B
package com.zcz.study.spi;
public class BDriver implements IDriver {
public void run() {
System.out.println("驱动 B");
}
}
实现主类
package com.zcz.study.spi;
import java.util.ServiceLoader;
public class SpiMain {
public static void main(String[] args) {
ServiceLoader<IDriver> drivers = ServiceLoader.load(IDriver.class);
for (IDriver d : drivers) {
d.run();
}
}
}
在resources下创建META-INF/services目录, 然后创建以接口全限定名为文件名的文件, 文件内写入需要被发现的实现类的全限定名(这步操作作为实现的发现)
最后执行结果
执行描述
全过程主要是通过ServiceLoader指定接口, 然后找到编译后的classes下的META-INF/services目录, 根据接口的全限定名文件读取实现类, 将实现类加载到JVM中, 最后实例化实现类返回(大概是这么个流程, 具体实现请看下面源码分析)
源码分析
从上面代码中知, SPI的核心操作类就是java.util.ServiceLoader
这个类, 这个类中的实现也不复杂, 连带注释一共587行,
ServiceLoader<IDriver> drivers = ServiceLoader.load(IDriver.class);
属性
//指定读取实现接口信息的文件位置
private static final String PREFIX = "META-INF/services/";
// 指定要发现实现的接口
private final Class<S> service;
// 类加载器(将实现类加载到jvm中)
private final ClassLoader loader;
// 安全上下文
private final AccessControlContext acc;
// 所有被发现的接口实现集合
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// 懒加载迭代器(ServiceLoader内部类)
private LazyIterator lookupIterator;
除AccessControlContext acc
外, 都不难理解!
初始化
以下按调用顺序从上至下
public static <S> ServiceLoader<S> load(Class<S> service) {
//初始化加载器
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){
//构造器构造serviceLoader
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
//判断接口是否存在, 若不存在 抛出异常 "Service interface cannot be null"
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
//初始化安全上下文
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
//初始实现类发现, 重新发现实现类
reload();
}
public void reload() {
//删除之前多有的接口实现, 重新加载接口实现
providers.clear();
//初始化迭代器
lookupIterator = new LazyIterator(service, loader);
}
初始化了 service, loader, acc, lookupIterator
类定义
public final class ServiceLoader<S> implements Iterable<S>
ServiceLoader类实现了Iterable接口, 这个类通过本类实例迭代器, 关联到属性
private LinkedHashMap<String,S> providers
中
public Iterator<S> iterator() {
return new Iterator<S>() {
//返回的迭代器, 其实是 providers 属性的迭代器
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();
//注意: 迭代器返回的结果是通过 LazyIterator迭代器返回的结果
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
注: ServiceLoader实现了Iterator接口, 方便用户通过serviceLoader遍历 实现
的集合, 并隐藏内部具体的实现, 自己本身的迭代器是通过迭代维护实现
的集合 providers 属性实现的, 在迭代providers时, 文件中发现的接口实现还没有被加载到JVM中, 在内部类LazyIterator中以懒加载的形式, 将接口实现类加载到JVM中, 并且实例化到providers中, 懒加载迭代器如下
迭代器
这是一个内部类, 通过迭代器间接调用LazyIterator的迭代方法, 在懒加载迭代器的迭代方法中, 通过反射对发现的实现类进行加载和实例化
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 {
//此处获取到MATE-INF/service 下的文件, 读取为多个资源文件路径Enumeration<URL>
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);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
SPI使用场景
实现策略模式, 但实现类希望动态加载的时候
哪些地方用到了SPI
数据库驱动,
dubbo服务发现:(对原生SPI做了进一步封装)
spring 中使用了SPI