前言
- spi的全称是【service provider】,是一个简单的服务加载机制,原理:在ServiceLoader.load的时候,根据传入的接口类,遍历META-INF/services目录下的以该类命名的文件中的所有类,并实例化返回。
- 在ServiceLoader中,提供了延迟按需实例化的机制,在加载器中,维护一个已经加载的服务列表缓存,每次调用iterator()方法时,会按照实例化顺序生成缓存的所有对象,然后懒加载并实例化剩余的提供者,并依次将每个provider添加到缓存中。可以通过reload()方法清除缓存。
官方文档:docs.oracle.com/javase/8/do…
举例
这是例子的代码结构
接口
public interface SpiService {
String test();
}
实现类
public class SpiAImpl implements SpiService {
@Override
public String test() {
return "AAAAAAAAAAAA";
}
}
public class SpiBImpl implements SpiService {
@Override
public String test() {
return "BBBBBBBBBBBBBB";
}
}
META-INF/services配置文件内容
在配置文件中,列出SpiService所有的实现类。如果只想加载SpiBImpl,那么可以删除SpiAImpl文件行。
com.example.spi.impl.SpiAImpl
com.example.spi.impl.SpiBImpl
加载并执行
@Test
public void contextLoads() {
ServiceLoader<SpiService> serviceLoader
= ServiceLoader.load(SpiService.class);
for (SpiService service : serviceLoader) {
System.out.println(service.test());
}
}
输出结果
源码研究
源码量很少,只有几百行,其中还有大量的注释,这里不做细致的介绍,只关注涉及到的关键点。
PREFIX静态变量,定义了spi加载的位置;providers是服务提供类的缓存。
private static final String PREFIX = "META-INF/services/";
// Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
加载源码
每次调用ServiceLoader.load()方法,都会执行reload(),清除providers缓存,并实例化懒加载迭代器,在调用iterator.hasNextService()方法时,再加载当前接口配置文件中,所有的服务实现类。
懒加载执行
private boolean hasNextService() {
// 获取文件地址
String fullName = PREFIX + service.getName();
// 加载资源文件
Enumeration<URL> configs = loader.getResources(fullName);
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
// 解析文件,BufferedReader按行读取
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
mysql驱动
mysql驱动也用到了SPI接口,在【DriverManager】类中,执行加载流程。
加载数据库驱动
DriverManager中,有个静态代码块,在类初始化时执行
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
在loadInitialDrivers方法中,会调用doPrivileged方法,来执行具体的加载动作。
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 加载数据库驱动
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
// 实例化驱动实现类
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});