zoukankan      html  css  js  c++  java
  • dubbo源码分析--dubbo spi解析

    1. 什么叫SPI?

    简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射。

    2. java SPI与Dubbo SPI有什么区别

    (此图来自网上,我没有刻意去截图)

    然后在这个文件里面写入实现类

    com.blueskykong.javaspi.serializer.KryoSerializer

    com.blueskykong.javaspi.serializer.JavaSerializer

    但是dubbo的SPI格式变了,也就意味着不能直接使用java SPI了

    文件的目录相似,但是里面的内容变为了key-value

    adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
    spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory

    结论:所谓的这些SPI只不过是一种方式而已,最后都是反射生成类(还有classloader的问题,这里我们不讨论),组织方式可以随便变,但是既然是分析dubbo,那么这里就看dubbo是怎么最终实现的

    3. 为什么dubbo要自己实现所谓的SPI?

    dubbo相当于使用了key-value的形式,用key来映射每个类,这样用户就可以在url加入这个key来实现动态加载某个类的需求,

    那么为什么java SPI的方式不行:

    • java SPI的文件里面仅仅只有类名,如果想让用户动态指定,那么需要按照这个类名来标识用户想要哪个类,太长,或者是在类名字上加上一定的区分规则
    • java SPI的加载是一次性会加载所有的类,但是并不是每个类都需要

    4. dubbo spi实现方式详解

    4.1 背景

    现在使用key-value的方式已经可以由用户指定key去加载class,但是出于系统的考虑还有一些功能需要扩展

    • key-value的指定是由@Adaptive和@SPI来辅助实现,@SPI可以指定类级别的默认的类,@Adaptive可以设置函数级别需要加载哪个class
    • 针对FIlter需要指定是否起效,以及在provider起效还是consumer起效,以及Filter之间的优先级

     4.2 源码

    dubbo中负责SPI的处理的类主要是 ExtensionLoader

    这里我标注出主要的变量含义

    /**
         * 这个三个代表需要从哪个resource路径下加载我们的key-value文件
         */
        private static final String SERVICES_DIRECTORY = "META-INF/services/";
    
        private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
    
        private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
    
        /**
         * 在注解里面的value是可以使用逗号(,)来设置多个值
         */
        private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");
    
        /**
         * ExtensionLoader 是每个接口对应一个 这个map保存了接口和对应的ExtensionLoader的对应
         */
        private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
    
        /**
         * 保存了配置文件中所有value-key之间的映射
         */
        private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
    
        // 以上都是静态变量也就是是全局的
    
        /**
         * 当前这个ExtensionLoader 对应的那个接口
         */
        private final Class<?> type;
    
        private final ExtensionFactory objectFactory;
    
        /**
         * value-key之间的反映射
         */
        private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
    
        /**
         * 配置文件中key-value之间的映射 value指的是类class
         */
        private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
    
        /**
         * 这里保存着这个接口的实现类含有@Activate注解
         */
        private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
        /**
         * 这个接口下 key-value的映射 value指的是实例
         */
        private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
    
        /**
         * 适配类的实例
         */
        private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
        /***
         * 这个接口的适配类 也就是注解@Adaptive的注解加载类的这个类就是适配类 所谓的适配类 就是实现根据url里面的参数
         * 来决定使用这个接口的哪个子类
         */
        private volatile Class<?> cachedAdaptiveClass = null;
        /**
         * SPI注解里面value 作为这个接口的默认加载类
         */
        private String cachedDefaultName;

    总结一下:

    1. 每一个接口比如Protocol和ExtensionLoader之间是一一对应的关,但是Protocol这个接口有很多子类,以及默认类,那么这些管理都是在ExtensionLoader里面管理,上面的大多数字段都是为了管理而存在
    2. ExtensionLoader最主要的功能是为了根据key来获取类,然后其他的都是一些变化。
    3. ExtensionLoader的主要功能和思想(为了实现key到value的转换),也就是说我想在有了key-value之间的关系,你可以认为这就是一个小型db,那么你想查询某一个接口的class有多少,以及每个class对应的key是什么,这些都只是功能的问题,核心是我有了关系,只要能从这些元数据推导出来,那么ExtensionLoader都可以做到。

    5. 高级

     5.1 包装类

      在key-value映射中,会分为包装类和正常类,所谓的包装类就是利用装饰器模式,来实现对原功能进行一些额外的处理,识别方式是按照是否有一个构造方法含有这个接口作为参数为标识

     5.2 注入

      这里的注入是指对接口里面的变量进行注入实例

    还在更新......

  • 相关阅读:
    小程序工程化探索:大规模场景下的问题和解决方案----------------引用
    对Taro Next小程序跨框架开发的探索与实践-----------------引用
    对Node.js 中的依赖管理------------引用
    对redux的研究--------引用
    JavaScript 中的 for 循环---------------引用
    对JavaScript 模块化的深入-----------------引用
    对Webpack 应用的研究-----------------引用
    webpack5持久化缓存
    设置x 轴斜体(每次我都百度,这次单独为它发一个)
    字典元组列表常用方法
  • 原文地址:https://www.cnblogs.com/shanchuan04/p/10231104.html
Copyright © 2011-2022 走看看