从自定义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) {
//..
}
//..
}
}
}
注解1
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