1. 背景
翻到之前做的spi源码的一些笔记,想分享一下
2. 什么是SPI
Java的SPI全称是Service Provider Interface,它是Java提供的一套用来被第三方实现或者扩展的接口,可以用来启用框架扩展和替换组件。SPI的作用就是为这些被扩展的API寻找服务实现。它是一种服务发现机制,其核心思想就是解耦,这在模块化设计中尤其重要。我们可以看一下jdbc中spi的使用:
当我们打开jdk的源码之后,我们发现rt.jar下有一个java.sql.Driver的类,它定义了一些数据库操作的标准方法。然后mysql-connector-java中的com.mysql.jdbc实现了java.sql.Driver接口。同时在META-INF/services目录下放了java.sql.Driver文件:
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
3. 一个demo
- 定义一个ISpi接口
public interface ISpi {
void print();
}
- 实现类SpiA
public class SpiA implements ISpi {
@Override
public void print() {
System.out.println(this.getClass().getCanonicalName());
}
}
- 实现类SpiB
public class SpiB implements ISpi {
@Override
public void print() {
System.out.println(this.getClass().getCanonicalName());
}
}
- 测试方法
public static void main(String[] args) {
ServiceLoader<ISpi> spi = ServiceLoader.load(ISpi.class);
for (ISpi iSpi : spi) {
iSpi.print();
}
}
- 打印
com.example.spi.impl.SpiA
com.example.spi.impl.SpiB
4. 源码 java.util.ServiceLoader
4.1 关键参数
//目录
private static final String PREFIX = "META-INF/services/";
//定义的接口
private final Class<S> service;
//类加载器
private final ClassLoader loader;
private final AccessControlContext acc;
//local cache
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
private LazyIterator lookupIterator;
4.2 load方法
- load方法
public static <S> ServiceLoader<S> load(Class<S> service) {
//获取当前线程的类加载器
ClassLoader cl = Thread.currentThread().getContextClassLoader();
//继续调用load
return ServiceLoader.load(service, cl);
}
- load
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
//创建一个新的ServiceLoader
return new ServiceLoader<>(service, loader);
}
- 构造函数
private ServiceLoader(Class<S> svc, ClassLoader cl) {
//常规判空
service = Objects.requireNonNull(svc, "Service interface cannot be null");
//传的类加载器为空,则使用系统类加载器,所以当想直接使用类加载器,就直接传null进来
//应该就是这里破坏了双亲委派,因为直接用当前的类加载机器加载了,而没有双亲委派
//是的,就是这里破坏了双亲委派,专业名称是:线程上下文类加载器,通过线程(Thread)类的setContextClassLoader()进行设置,未设置会从父线程继承,默认是应用程序类加载器
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
//java的某种安全机制
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
//清空map容器和初始化迭代器
reload();
}
- reload 方法
public void reload() {
//清空map容器,因为这个方法是用户可以调用的,不是private方法
providers.clear();
//初始化迭代器 -- 懒加载
// LazyIterator 的构造方法里没有处理逻辑只有赋值类加载器和类变量
lookupIterator = new LazyIterator(service, loader);
}
4.3 编译后的main方法
我们可以看到编译后,实际调用的是哪个方法
public static void main(String[] args) {
ServiceLoader<ISpi> spi = ServiceLoader.load(ISpi.class);
Iterator var2 = spi.iterator();
while(var2.hasNext()) {
ISpi iSpi = (ISpi)var2.next();
iSpi.print();
}
}
4.4 iterator
//调用ServiceLoader 的iterator 方法获取一个新的迭代器
public Iterator<S> iterator() {
return new Iterator<S>() {
//map迭代器,那么这个map应该是一个local cache
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();
}
//不支持remove
public void remove() {
throw new UnsupportedOperationException();
}
};
}
4.4 hasNext方法
//首先看hashNext,map的就不看了
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);
}
}
4.5 hasNextService 方法
private boolean hasNextService() {
//nextName 不为空直接返回
if (nextName != null) {
return true;
}
//获取spi文件的url
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);
}
}
//把文件的每一行作为一个类名读到pending 中
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
4.6 next方法
//然后是读下一条
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);
}
}
4.7 nextService
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());
//local cache ps:终于看到这个逻辑了
providers.put(cn, p);
//返回
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
5. 总结
写文章的过程中,也是重新复习了一下之前看的代码。