欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

dubbo源码分析--dubbo spi解析

程序员文章站 2022-04-04 14:54:56
1. 什么叫SPI? 简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射。 2. java SPI与Dubbo SPI有什么区别 (此图来自网上,我没有刻意去截图) 然后在这个文件里面写入实现类 com.blueskykong.javaspi.serializer.KryoSer ......

1. 什么叫spi?

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

2. java spi与dubbo spi有什么区别

dubbo源码分析--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 注入

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

 

 

 

还在更新......