扩展阅读
SPI简介
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。
可扩展的解决方式有
factory工厂 ,ioc容器 ,osgi容器 都是重量级的
dubbo不希望依赖其它容器 ,所以参考jdk的spi实现方式 自己实现了一套。
在理解Dubbo的SPI之前,要明确几个核心概念:
-
扩展点 Dubbo作用灵活的框架,并不会强制所有用户都一定使用Dubbo提供的某些架构。例如注册中心(Registry),Dubbo提供了zk和redis,但是如果我们更倾向于其他的注册中心的话,我们可以替换掉Dubbo提供的注册中心。针对这种可被替换的技术实现点我们称之为扩展点,类似的扩展点有很多,例如Protocol,Filter,Loadbalance等等。
-
Wrapper Dubbo在加载某个接口的扩展类时候,如果某个实现中有一个拷贝类构造函数,那么该接口实现就是该接口的包装类,此时Dubbo会在真正的实现类上层包装上盖Wrapper。即这个时候从ExtensionLoader中返回的实际扩展类是被Wrapper包装的接口实现类。
-
Adaptive 这个自适应的扩展点比较难理解,所以这里直接以一个例子来讲解:在RegistryProtocol中有一个属性为Cluster,其中Protocol和Cluster都是Dubbo提供的扩展点,所以这时候当我们真正在操作中使用cluster的时候究竟使用的哪一个cluster的实现类呢?是FailbackCluster还是FailoverCluster?Dubbo在加载一个扩展点的时候如果发现其成员变量也是一个扩展点并且有相关的set方法,就会在这时候将该扩展点设置为一个自适应的扩展点,自适应扩展点(Adaptive)会在真正使用的时候从URL中获取相关参数,来调用真正的扩展点实现类。具体的实现会在下面的源码中详细解释。对于Adaptive的理解其实个人推荐的是Dubbo开发者指南,指南中有对于Adaptive的明确介绍。
-
Activate 官网的叫法是自激活,其实这个更合适的叫法我认为是条件激活,我们还记得上一篇中有提到Filter的内容,其中Filter链的获取就是通过@Activate注解来确定的,所以Activate的作用主要是:提供一种选择性激活的条件,可以是我们通过相关的配置来确定激活哪些功能。
ExtensionLoader
我们直接看代码
服务提供者创建类
在ServiceConfig加的的时候 执行里面的静态初始代码
public class ServiceConfig<T> extends AbstractServiceConfig {
private static final long serialVersionUID = 3033787999037024738L;
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
ExtensionLoader 类为dubbo spi实现的类
类上必须有SPI注解
大量用到了本地缓存
如果缓存里没有ExtensionFactory会通过ExtensionLoader.getExtensionLoader先创建ExtensionFactory
injectExtension方法是dubbo的 DI
然后会执行到这个方法
第一个加载的文件是
因为AdaptiveExtensionFactory 类有 @Adaptive 赋值给 cachedAdaptiveClass ,其它的放到cachedNames里
继续回到 getAdaptiveExtensionClass().newInstance() 就是调用 AdaptiveExtensionFactory构造方法
1.getExtension(key)方法很好理解,从本地cachedNames的map中找到对应key的class对象,通过反射创建该class对象的实例对象并返回
2.getAdaptiveExtension()方法复杂一些,如果cachedAdaptiveClass 为空 (dubbo扩展文件中没有配置带有@Adaptive的类) 会通过字符串拼接出一个java文件文本
调用createAdaptiveExtensionClass方法
创建自适应拓展类
生成的类
@Adaptive
extensionLoader.getExtension
方法写死了扩展点对应的实现类,不能在程序运行期间根据运行时参数进行动态改变。
对于这个矛盾的问题,就有了Adaptive就是
dubbo自适应机制。
它可以修饰在类上,也可以修饰在方法上面。这两者有什么区别呢?
简单来说,放在类上,说明当前类是一个确定的自适应扩展点的类。
dubbo只有2个类上有Adaptive注解修饰,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory (加载扩展)
AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory
Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory
如果是放在方法级别,那么需要生成一个动态字节码,来 进行转发。
比如拿 Protocol 这个接口来说,它里面定义了 export 和 refer 两个抽象方法,这两个方法分别带有@Adaptive 的标识,标识是 一个自适应方法。 我们知道 Protocol 是一个通信协议的接口,具体有多种实现,那么这个时候选择哪一种呢? 取决于我们在使用 dubbo 的时候所 配置的协议名称。而这里的方法层面的 Adaptive 就决定了当前这个方法会采用何种协议来发布服务。
由于上面的逻辑比较深,下面给出简单文字逻辑:
1.为了获得一个扩展点的适配类,首先会看缓存中有没有已经加载过的适配类,如果有的话就直接返回,没有的话就进入第2步。
2.加载所有的配置文件,将所有的配置类都load进内存并且在ExtensionLoader内部做好缓存,如果配置的文件中有适配类就缓存起来,如果没有适配类就自行通过代码自行创建适配类并且缓存起来(代码之后给出样例)。
3.在加载配置文件的时候,会依次将包装类,自激活的类都进行缓存。
4.将获取完适配类时候,如果适配类的set方法对应的属性也是扩展点话,会依次注入对应的属性的适配类(循环进行)。
参考
https://www.jianshu.com/p/dc616814ce98