【Dubbo】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) {
                //..
            }
            //..
        }
    }
}

注解1
注解1

load方法关注最后一步关注实例化一个LazyIterator实例,赋值给变量lookupIterator

注解2

注解2

iterator方法就是实例化了一个匿名类

注解3

注解3

hasNext方法最终调用的就是这里,从中我们可以看到fullName的PREFIX就是我们在resources下创建的文件夹META-INF/services/与service的全路径名字拼接,所以我们创建的文件是固定的,然后通过getResources获取内容,nextElement读取下一个

注解4

注解4

nextName在我们执行hasNext的时候已经赋上值了,这里我们通过Class.foorName进行实例化,但是这时候先不初始化,接下来需要判断他的继承关系,接着通过newInstance实例化放入到providers这个缓存map中

JDBC的SPI

JDBC-SPI

我们看到JDBC就使用了SPI的技术,而且使用的就是JDK原生的方式

JDBC-SPI1

JDBC-SPI2

Dubbo的SPI

Jdk的SPI就是一个List,你只能通过Iterator进行遍历获取你想要的元素,Dubbo自定义了自己的SPI,使用的是map这种K/V结构的数据结构,这样方便快速定位到想要的实现类。

dubbo的spi原理我觉得官网讲的不错,直接贴链接dubbo-spi原理

dubbo的spi大大提高了dubbo的灵活性,假如你对注册中心不满意,或者你对rmi协议想定制了,这时候你只需要自定义自己的模块即可

dubbo-spi

然后你在配置文件中直接使用你自定义的名字即可,这里的csrmi其实就是你在META-INF/dubbo/internal定义的文件中的key

参考

Java SPI详解
Spring SPI详解

posted @ 2020-09-15 17:59  colin_xun  阅读(118)  评论(0编辑  收藏  举报