Dubbo 的 SPI
从自定义 Dubbo 的 rpc 协议来学习 Dubbo 的 SPI
SPI 全称为 Service Provider Interface
JDK 的 SPI 实现
public class SpiTest {
public static void main(String[] args) {
// 注解1
ServiceLoader<Human> load = ServiceLoader.load(Human.class);
// 注解2
Iterator<Human> iterator = load.iterator();
// 注解3
while (iterator.hasNext()) {
// 注解4
Human next = iterator.next();
System.out.println(next.getName());
}
}
}
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 java.util.ServiceLoader.LazyIterator lookupIterator;
// 私有内部类,提供对所有的service的类的加载与实例化
private class LazyIterator implements Iterator<S> {
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
String nextName = null;
//...
private boolean hasNextService() {
if (configs == null) {
try {
//获取目录下所有的类
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
//...
}
//....
}
}
private S nextService() {
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
//反射加载类
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
}
try {
//实例化
S p = service.cast(c.newInstance());
//放进缓存
providers.put(cn, p);
return p;
} catch (Throwable x) {
//..
}
//..
}
}
}
load 方法关注最后一步关注实例化一个 LazyIterator 实例,赋值给变量 lookupIterator
注解 2
iterator 方法就是实例化了一个匿名类
注解 3
hasNext 方法最终调用的就是这里,从中我们可以看到 fullName 的 PREFIX 就是我们在 resources 下创建的文件夹 META-INF/services/ 与 service 的全路径名字拼接,所以我们创建的文件是固定的,然后通过 getResources 获取内容,nextElement 读取下一个
注解 4
nextName 在我们执行 hasNext 的时候已经赋上值了,这里我们通过 Class.foorName 进行实例化,但是这时候先不初始化,接下来需要判断他的继承关系,接着通过 newInstance 实例化放入到 providers 这个缓存 map 中
JDBC 的 SPI
我们看到 JDBC 就使用了 SPI 的技术,而且使用的就是 JDK 原生的方式
Dubbo 的 SPI
Jdk 的 SPI 就是一个 List,你只能通过 Iterator 进行遍历获取你想要的元素,Dubbo 自定义了自己的 SPI,使用的是 map 这种 K/V 结构的数据结构,这样方便快速定位到想要的实现类。
dubbo 的 spi 原理我觉得官网讲的不错,直接贴链接 dubbo-spi 原理
dubbo 的 spi 大大提高了 dubbo 的灵活性,假如你对注册中心不满意,或者你对 rmi 协议想定制了,这时候你只需要自定义自己的模块即可
然后你在配置文件中直接使用你自定义的名字即可,这里的 csrmi 其实就是你在 META-INF/dubbo/internal 定义的文件中的 key
参考
【Dubbo】Dubbo 的 SPI - colin_xun - 博客园









浙公网安备 33010602011771号