SPI(Service Provider Interface),是 JDK 内置的一种服务提供发现机制,是一种策略模式的实现方式。
通过在 ClassPath 路径下的 META-INF/services
文件夹查找文件,自动加载文件里所定义的类。
SPI 基础使用
我们在 java 同级的目录下,创建 META-INF/services
目录,然后以接口的全限定名创建文件:com.example.spidemo.SPIService
文件中定义了接口的具体实现类:
com.example.spidemo.SpiImpl1
com.example.spidemo.SpiImpl2
com.example.spidemo.SpiImpl3
那么,我们就可以使用 ServiceLoader
获取到这个 SPIService
的具体实现了。
public class SpiDemoTest {
@Test
public void test() {
ServiceLoader<SPIService> loader = ServiceLoader.load(SPIService.class);
for (SPIService spiService : loader) {
spiService.execute();
}
}
}
源码分析
ServiceLoader.load() 的执行过程
核心是,创建了一个 LazyIetrator 对象,真正通过反射创建实现类的方法不在这里。
// 我们没传入 classloader,获取当前线程的 classloader
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
// new 出来一个 ServiceLoader,两个参数:接口名 & classloader
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
// 在构造方法中,会调用 reload 方法
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
// Android-changed: Do not use legacy security code.
// On Android, System.getSecurityManager() is always null.
// acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
// 这里 new 出来一个 LazyIterator
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
// LazyIterator 只是存储了两个参数,没有实际的逻辑
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
缓存机制
ServiceLoader 的 iterator 方法实现如下,这里实现了一个简单的缓存机制。
knownProviders
是缓存 providers
的迭代器,在遍历时,会优先从 knownProviders
中读取缓存,如果读到了,就直接返回;如果没读到,再从 loopupIterator
中去读取。
loopupIterator
就是上面的 LazyIterator
。
// ServiceLoader.java
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();
}
};
}
LazyIterator 的遍历过程
// LazyIterator 本身实现了 Iterator 接口
private class LazyIterator
implements Iterator<S>
{}
// 其 hasNext 方法和 next 方法分别调用了 `hasNextService` 方法和 `next` 方法
public boolean hasNext() {
// Android-changed: do not use legacy security code
/* 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() {
// Android-changed: do not use legacy security code
/* if (acc == null) { */
return nextService();
/*
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
*/
}
hasNextService 方法实现
private boolean hasNextService() {
// 如果已经有缓存的 nextName 变量,那么直接返回 true
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 变量中
nextName = pending.next();
return true;
}
文件解析的实现
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;
// 解析每一行内容,放入 names 数组中
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);
}
}
// 返回 names 数组的迭代器
return names.iterator();
}
// 解析每一行内容的代码:
private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
List<String> names)
throws IOException, ServiceConfigurationError
{
// 读取到每一行内容
String ln = r.readLine();
if (ln == null) {
return -1;
}
// 取第一个 「#」 之前的内容
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
int n = ln.length();
if (n != 0) {
// 异常检查:不能包含空格和 '\t'
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
fail(service, u, lc, "Illegal configuration-file syntax");
// 异常检查:检查每一个字符是否都合法
int cp = ln.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
// 将类名添加到 names 数组中
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);
}
// 返回下一行行号
return lc + 1;
}
nextService 实现
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
// 将 nextName 读取到临时变量
String cn = nextName;
// 读取后,清楚 nextName 的值
nextName = null;
Class<?> c = null;
try {
// 通过反射,找到 cn 对应的 class 对象
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
// Android-changed: Let the ServiceConfigurationError have a cause.
"Provider " + cn + " not found", x);
// "Provider " + cn + " not found");
}
// 判断 c 这个类是不是 service 接口的一个实现类,如果不是,报错
if (!service.isAssignableFrom(c)) {
// Android-changed: Let the ServiceConfigurationError have a cause.
ClassCastException cce = new ClassCastException(
service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
fail(service,
"Provider " + cn + " not a subtype", cce);
// fail(service,
// "Provider " + cn + " not a subtype");
}
try {
// 实例化实现对象,并强转成 service 类型
// 这里 S 是泛型,其实指的就是 service 的类型
S p = service.cast(c.newInstance());
// 缓存到 LinkedHashMap 中
providers.put(cn, p);
// 返回
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
网友评论