1、创建
// SPI接口实现类 要加载的位置前缀
private static final String PREFIX = "META-INF/services/";
// 要加载接口的class对象
// The class or interface representing the service being loaded
private final Class<S> service;
// 加载器
// The class loader used to locate, load, and instantiate providers
private final ClassLoader loader;
// 权限访问控制
// The access control context taken when the ServiceLoader is created
private final AccessControlContext acc;
// 缓存 提供方的
// Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// 懒查找迭代器
// The current lazy-lookup iterator
private LazyIterator lookupIterator;
上述为 ServiceLoader成员属性
那ServiceLoad.load 干了什么??
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) {
// 判断一下传入的接口class对象是否合法
service = Objects.requireNonNull(svc, "Service interface cannot be null");
// 类加载器,如果线程的classLoad没有,默认采用SystemClassLoader
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
// 权限访问控制
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
public void reload() {
// 先把之前的缓存清了
providers.clear();
// 创建懒迭代器对象。
lookupIterator = new LazyIterator(service, loader);
}
reload方法, 先是将缓存清了,又创建懒 迭代器对象。这个懒加载迭代器是ServiceLoader的一个内部类。
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 {
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();
}
}
ServiceLoader.load核心是清除缓存,创建lazyInterator,其用来类加载,遍历SPI实现类
2、加载
加载是在 LazyIterator 中完成的, 而且是在当我们判断获取的时候才加载
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);
}
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// 拼接 路径 META-INF/services/spi接口名称
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
// 获取spi接口实现类url
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
// 第一次的时候 或者 pending没有 下一个元素的时候
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
// 获得一个 迭代器。
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
可以看出进入一个hasNextService方法。在hasNextService方法中,先是判断一下下一个的元素名有没有,有的话直接返回true。判断config ==null 这个第一次的时候会进入,拼接默认spi接口实现类存放的路径,形成一个url。接着就会解析这个文件,获得一个迭代器对象。这个url实际上就是spi接口文件地址
说白了,上述操作,就是想获取到要加载的指定SPI实现类文件,获取到文件,读取配置项,也即获取SPI接口实现类列表
private Iterator<String> parse(Class<?> service, URL u)
throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
// 存储 扩展实现类的接口的全类名
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
// 通过BufferedReader来一行一行的读取
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
通过BufferedReader来一行一行的读取
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
// 最后返回 集合的迭代器
return names.iterator();
}
parse(service, configs.nextElement())方法。我们可以看出parse方法通过BufferedReader 一行行读取配置文件存入List中,最后返回List的迭代器。
加载的过程,就是找到SPI接口位置,读取SPI接口配置文件,获取其中的实现类
3、获取
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);
}
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
//保存副本
String cn = nextName;
// 设置null
nextName = null;
Class<?> c = null;
try {
// 生成class对象
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
}
获取SPI实现类对象,本质上通过Class.forname 进行类加载获取的,然后放入缓存。
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();
}
};
}
经过第一次SPI类加载之后,后续所有遍历操作都直接从缓存中拿,除非重新进行ServiceLoader.load 重新读取SPI接口文件配置项,进行类加载
总结
SPI机制是java提供的扩展机制,主要用来为第三方应用进行扩展用的,自身服务只需要提供SPI接口,第三方应用自己实现SPI接口即可。
SPI原理无非是内部通过LazyInterator进行处理,先找到SPI配置文件地址,逐一读取配置项,进行类加载获取class。当然内部维护一套缓存机制provider,不需要每次都读取SPi配置文件,Class.ForName,优化性能
优点:
SPI可以说是一种插拔机制, 使用SPI可以实现解耦,可以使得调用者与服务者自由扩展,而不是耦合在一起,可以使应用程序能够根据业务需要启用框架扩展或者替换框架组件