首页 > 其他 > 详细

Dubbo的SPI

时间:2020-09-15 20:09:40      阅读:71      评论:0      收藏:0      [点我收藏+]

从自定义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

参考

Java SPI详解

Dubbo的SPI

原文:https://www.cnblogs.com/colin-xun/p/13674465.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!