Java SPI机制

Service Provider Interface

提供接口,可替换组件实现,插件扩展,服务发现机制
META-INF/services/接口全限定名文件中指定实现类,文件可以有多行每行一个实现类

ServiceLoader源码实现


ServiceLoader设计成一个迭代结构,即可以加载同一接口的多个实现

  • 调用load只是构建了迭代器,并且延迟加载,真正遍历时才加载
  • 加载到类名字符串后采取forName获取类,反射newInstance获取实例
  • 缓存机制,加载过一次就把实例缓存起来,再次加载直接读缓存
ServiceLoader.java
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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 LazyIterator lookupIterator;


//默认采用当前线程的加载器,即使用AppClassLoader
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) {
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();
}
//实际上创建了一个Lazy的迭代器,即使用时采取寻找加载
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}

//缓存机制,只有已知的都找过了,才真正尝试查找
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();
}
};
}
}

查找细节

ServiceLoader.java
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private class LazyIterator implements Iterator<S> {
Class<S> service;
Enumeration<URL> configs = null;
//...
//hasNext判断有无时尝试查找
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//就是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 = pending.next();
return true;
}

//next时加载
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 {
//实例化并强转SPI接口
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();
}
}

SPI应用


JDBC

DriverManager.java
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class DriverManager {
//静态初始化块,尝试加载Driver
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}

private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
//先尝试环境变量jdbc.drivers
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}

AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
//SPI机制加载Driver
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();

try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
}
//加载后直接返回,即优先采取SPI机制
return null;
}
});

//...
//继续解析jdbc.drivers
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
//类加载并且初始化
Class.forName(aDriver, true, ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
//...
}

Mysql的驱动器包为mysql-connector-java.jar
内部包含META-INF/services/java.sql.Driver文件
文件内容为com.mysql.cj.jdbc.Driver
SPI机制进行类加载,会触发驱动类下静态初始化块,把驱动注册到JDBC的DriverManager中

Driver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}

public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}

整体流程

  1. JDBC加载DriverManager
  2. DriverManager静态初始化块寻找Driver触发SPI
  3. SPI加载到Mysql的Driver类
  4. Driver静态初始化块内创建实例,注册到DriverManager