• 【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详解

  • 相关阅读:
    ECS:Systems
    ECS:Components
    ECS:Entitias
    对List遍历过程中添加和删除的思考
    SpringBoot 推荐博客
    理解和解决Java并发修改异常ConcurrentModificationException(转载)
    我对CopyOnWrite的思考
    最简日志打印规范(推荐,转载)
    为什么不应该使用Zookeeper做服务发现?(转载)
    浏览器允许跨域设置(不用于生产环境,开发用)
  • 原文地址:https://www.cnblogs.com/colin-xun/p/13674465.html
Copyright © 2020-2023  润新知