这一章的复习主要涵盖:
- JAVA SPI 的概述
- SPI 简易 demo 实现
- SPI 源码及实现原理分析
如果有不准确的地方,还望大佬在评论区指出,感激不尽。
文章中的所有的自定义的测试都在自己的 java-review
仓库中,本文对应的链接:
JAVA SPI
SPI 的全称是
Service provider interface
Demo
目录结构
java spi 的固定的目录结构为:
1 | tree src |
resource/META-INF/services 目录下,创建一个接口的全类名的纯文本文件,例如这里的: org.example.test.javase.spi.api.HelloService
,其内容为实现类的类名,按照行的单位进行排列,例如这里的:org.example.test.javase.spi.impl.HelloServiceImpl
1 | cat src/main/resources/META-INF/services/org.example.test.javase.spi.api.HelloService |
示例代码
HelloService
接口1
2
3
4
5
6
7
8
9
10package org.example.test.javase.spi.api;
public interface HelloService {
/**
* say hello
*/
void sayHello();
}HelloServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12package org.example.test.javase.spi.impl;
import org.example.test.javase.spi.api.HelloService;s
public class HelloServiceImpl implements HelloService {
public void sayHello() {
System.out.println("hello world");
}
}
`Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package org.example.test.javase.spi;
import java.util.ServiceLoader;
import org.example.test.javase.spi.api.HelloService;
public class Main {
public static void main(String[] args) {
ServiceLoader<HelloService> services = ServiceLoader.load(HelloService.class);
for (HelloService service : services) {
service.sayHello();
}
}
}
实现原理
java.util.ServiceLoader#load()
方法返回的对象虽然是 ServiceLoader 类型,但其实现的迭代器接口获取迭代对象的时候,就会通过反射创建对应对象实例
调用堆栈
1 | `----java.util.ServiceLoader:load() |
链路分析
ServiceLoader#load 可以手动指定类加载器,默认使用当前线程的类加载器
1
2
3
4
5
6
7
8
9
10
11
12
13// java.util.ServiceLoader
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader) {
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}调用 reload 方法清空 providers,并且重置 lookupIterator 为一个新的懒加载迭代器,这一个作为返现迭代器,负责根据上述的目录结构进行查找加载类
1
2
3
4
5
6// java.util.ServiceLoader
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}调用 ServiceLoader 对象实例的 iterator 获取到迭代器,类加载迭代 providers 属性;这里使用匿名内部类 new Iterator<S> 和 LazyIterator 两种迭代器是为了避免类每次调用时的加载而进行的缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// java.util.ServiceLoader
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();
}
};lookupIterator 查询迭代器用于从配置中加载类
ServiceLoader#iterator() 获得的迭代器为 providers 获取的迭代器
首先会遍历完 providers 中的对象,然后遍历 lookupIterator 延迟加载
LazyIterator lookupIterator 查找迭代器中的 hasNext() 和 next() 方法都使用 PrivilegedAction 对操作进行了封装,保证在开启了权限的时候才会执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// java.util.ServiceLoader.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);
}
}
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);
}
}通过迭代获取对象,触发类加载机制,hasNextService 和 nextService 是 spi 的核心方法
ServiceLoader#hasNextService 方法中得到的 config 对象是一个
CompoundEnumeration<E>
内置属性Enumeration<URL>[] enums
迭代器数组,configs
对象通过 CompoundEnumeration#hasMoreElements 方法迭代的时候,会通过下标迭代enums
属性中的所有成员的所有迭代器,enums[0].next()
保存的就是根据fullName
获取到的文件 URL1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// java.util.ServiceLoader.LazyIterator
private static final String PREFIX = "META-INF/services/";
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;
}ServiceLoader#parse 方法通过 URL 获取到 stream,按行将其保存到 name 中,返回其迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// java.util.ServiceLoader.LazyIterator
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();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
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();
}nextService 方法根据全类名完成类加载后,通过反射创建对象实例并转化为对应的 Class<T> 的泛型类型后返回对象实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// java.util.ServiceLoader.LazyIterator
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
}