Dubbo SPI机制

SPI机制

SPI 全称为(Service Provider Interface),一种动态替换 发现 服务实现者 的机制.

能在运行时,动态给接口添加实现类.

通过改变配置文件,就能动态的改变一个接口的实现类.

Dubbo里面有很多个组件,每个组件在框架中都是以接口的形成抽象出来.具体的实现又分很多种,在程序执行时根据用户的配置来按需取接口的实现.

Dubbo实现的SPI与JDK自带的SPI的区别

摘自官网

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源.

  • 如果扩展点加载失败,连扩展点的名称都拿不到了.
    比如: JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因.

  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点.

  • JDK的SPI要用for循环,然后if判断才能获取到指定的SPI对象,Dubbo用指定的key就可以获取

  • JDK的SPI不支持默认值,Dubbo增加了默认值的设计

  • Dubbo SPI增加了IoC,AOP的支持,一个扩展可以直接setter注入其他扩展.

SPI配置

只有在接口打了@SPI注解的接口类才会去查找扩展点实现.

会依次从这几个文件中读取扩展点

META-INF/Dubbo/internal/   // Dubbo内部实现的各种扩展都放在了这个目录了
META-INF/Dubbo/
META-INF/services/

比如

  1. Protocol接口,接口上打了@SPI注解,默认拓展点名字为Dubbo
@SPI("Dubbo")
public interface Protocol{

}
  1. 查找Dubbo-rpc-default模块下META-INF/Dubbo/internal/com.alibaba.Dubbo.rpc.Protocol中的配置
Dubbo=com.alibaba.Dubbo.rpc.protocol.Dubbo.DubboProtocol
  1. 会用ExtensionLoader类加载实现
    Dubbo=com.alibaba.Dubbo.rpc.protocol.Dubbo.DubboProtocol

Dubbo 拓展点加载机制相关注解

@SPI

@Adaptive

@Activate

@Adaptive 注解

@Adaptive 注解的作用

  1. 在类上加 @Adaptive 注解的类,会创建对应类型Adaptive类缓存起来.

  2. 在方法上加 @Adaptive 注解,会(默认用Javassist)生成动态编译的Adaptive类(可以打断点在ExtensionLoader.createAdaptiveExtensionClassCode()中看到生成的类)

ExtensionLoader.loadFile()时判断: 判断类名是否含有@Adaptive注解.若有,则将此类作为适配类 缓存起来. (如果一个接口的所有实现类都没有类上打上这个注解的情况,则会用javassist生成一个Adaptive类)
代码里搜了一下,有 AdaptiveExtensionFactory, AdaptiveCompiler 这两个类上打了 @Adaptive 注解.

Wrapper

Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现.它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外.即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类.

扩展点的 Wrapper 类可以有多个,也可以根据需要新增.

通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中.新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点.

ExtensionLoader

ExtensionLoader 是扩展点载入器,用于载入 Dubbo 中的各种可配置组件,比如: 动态代理方式(ProxyFactory),负载均衡策略(LoadBalance),RCP协议(Protocol),拦截器(Filter),容器类型(Container),集群方式(Cluster)和注册中心类型(RegistryFactory)等.

获取 ExtensionLoader 流程

- ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class)
    根据拓展点的接口,获得拓展加载器.如根据Protocol接口获取它对应的ExtensionLoader
    - ExtensionLoader.getExtensionLoader(Class<T> type)中
    - 1.type必须是接口
    - 2.type上包含@SPI注解
    - 3.根据type尝试从EXTENSION_LOADERS缓存中获取ExtensionLoader
    - 4.在3中获取不到,则new ExtensionLoader<T>(type)创建并缓存起来
        - ExtensionLoader(Class<?> type)构造器中,若传入的type不是ExtensionFactory类型,则会获取ExtensionFactory的adaptiveExtension赋值到当前ExtensionLoader对象的objectFactory属性中,这个objectFactory类似于IoC容器,用于向拓展对象注入依赖的属性.
            - // 参见objectFactory创建的逻辑
    - 5.返回ExtensionLoader

获取 AdaptiveExtension 流程

- Protocol adaptiveExtension = loader.getAdaptiveExtension()
    注解在接口的方法上,SPI机制可以根据接口动态地生成自适应类 xxx$Adaptive,并实例化这个类并返回
    - ExtensionLoader.getAdaptiveExtension()中
    - 1.先尝试从cachedAdaptiveInstance缓存中获取adaptive对象
    - 2.在1中获取不到,则createAdaptiveExtension()创建adaptive对象并缓存起来
        - ExtensionLoader.createAdaptiveExtension()中
        - 1.(T) getAdaptiveExtensionClass().newInstance()
            获取adaptive拓展类
            - ExtensionLoader.getAdaptiveExtensionClass()中
            - 1.getExtensionClasses()
                获取所有拓展实现类
                - ExtensionLoader.getExtensionClasses()中
                - 1.尝试从cachedClasses中获取拓展实现类数组
                - 2.获取不到则classes = loadExtensionClasses()从配置文件中加载拓展实现类列表并缓存
                    - ExtensionLoader.loadExtensionClasses()中
                    - 1.从接口,如Protocol接口上获取@SPI注解的value属性值,如 `@SPI("dubbo"),value` 就是 dubbo,作为默认拓展名
                    - 2.分别从META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/路径下读取配置文件,加载拓展实现类列表
                        - ExtensionLoader.loadFile()中
                        - 1.根据文件名用类加载器获取urls数组
                        - 2.遍历urls数组,用流一行行读取配置文件
                        - 3.1.根据配置的类名创建Class对象
                        - 3.2.判断class上是否含有@Adaptive注解.若有,缓存到cachedAdaptiveClass中
                        - 3.3.若实现类中没有@Adaptive注解
                        - 3.3.1.判断实现类是否存在入参为接口的构造器(比如ProtocolFilterWrapper类是否还有入参为Protocol接口的构造器,public ProtocolFilterWrapper(Protocol protocol),若有的话说明它是Wrapper类),添加到wrappers中
                        - 3.3.2.既不是适配对象(@Adaptive),也不是wrapped的对象,那就是扩展点的具体实现对象,可以有多个.缓存到cachedNames和extensionClasses中,分别为class -> name 的映射 和 name -> class 的映射
            - 2.在1中,若配置的类上有@Adaptive注解,则缓存到cachedAdaptiveClass中.此处取到直接返回cachedAdaptiveClass.若没有adaptive类,则走下面生成adaptive类
            - 3.createAdaptiveExtensionClass()
                自动生成自适应拓展的代码实现,并编译后返回该类
                - ExtensionLoader.createAdaptiveExtensionClass()中
                - 1.createAdaptiveExtensionClassCode()
                    生成Adaptive类的代码code
                - 2.利用dubbo的SPI扩展机制获取Compiler的适配类,此处为AdaptiveCompiler对象
                - 3.compiler.compile(code, classLoader)编译上面生成的adaptive代码
                    - AdaptiveCompiler.compile()中
                    - 1.compiler = loader.getDefaultExtension()获得默认的 Compiler 拓展对象
                    - 2.compiler.compile(code, classLoader)编译类
                        - AbstractCompiler.compile()
                            - JavassistCompiler.doCompile()
                - 4.返回编译后的class
        - 2.injectExtension()
            注入依赖
            - // 参见AdaptiveExtensionFactory注入依赖的流程
        - 3.创建 Wrapper 拓展对象
            - // 参见SPI aop流程
    - 3.返回adaptive对象

根据name获取对象的流程

- Protocol dubboProtocol = loader.getExtension("dubbo");
    返回指定名字的扩展对象
    - ExtensionLoader.getExtension(name)中
    - 1.从cachedInstances获取对应的拓展对象
    - 2.若获取不到,创建Holder对象,并缓存
    - 3.通过instance = createExtension(name)填充holder.value属性
        - 1.之前的从配置文件中加载拓展类的逻辑
        - 2.缓存到EXTENSION_INSTANCES中
        - 3.注入依赖的属性injectExtension(instance)
            - ExtensionLoader.injectExtension()中
            - 1.遍历class中的所有方法找到setter方法
            - 2.对于每一个setter方法,// TODO
- objectFactory(即AdaptiveExtensionFactory)创建的逻辑
    - 在ExtensionLoader的构造方法中会判断当前传入的对象类型
    - 1.如果type是ExtensionFactory类型,则objectFactory设为null
    - 2.type不是ExtensionFactory类型,则会创建ExtensionFactory objectFactory用于后面给 拓展对象 注入依赖的属性
        - 1.从配置文件中获取adaptive类的逻辑...
        - 2.AdaptiveExtensionFactory构造器中
        - 获取ExtensionFactory的ExtensionLoader,用loader获取支持的extensions,此处为SPIExtensionFactory和SpringExtensionFactory
- AdaptiveExtensionFactory注入依赖的流程
    - 当有实例调用injectExtension(instance)要注入依赖时,会遍历这个对象里的所有方法,找出setter方法,如 setXxx(X xxx) 则传入参数类型 和 参数名
    - AdaptiveExtensionFactory.getExtension(type, name)中
        - 1.遍历SPIExtensionFactory和SpringExtensionFactory
        - 2.factory.getExtension(type, name)获取该属性的extension
            - SPIExtensionFactory.getExtension(type, name)中
            - 1.类型必须是接口且有@SPI注解,才会通过ExtensionLoader加载adaptiveExtension拓展,否则返回null
    - 如果有adaptiveExtension拓展,则反射调用setter方法注入

SPI aop流程

- SPI aop流程
    - 1.遍历从配置文件中加载的wrapperClasses
    - 2.1.使用带参的构造器创建Wrapper对象
    - 2.2.使用wrapper对象调用injectExtension()返回instance
    - 2.3.每遍历一次,就将wapper对象嵌套了一层
- SPI @Activate缓存到cachedActivates
    - ExtensionLoader.loadFile()里判断类上有Activate注解,则添加到缓存中.

SPI @Activate流程

- SPI @Activate流程
    - ExtensionLoader.getActivateExtension(url, values, group)
    - 1.通过getExtensionClasses()获得拓展实现类列表
    - 2.遍历cachedActivates
    - 2.1.isMatchGroup()判断activate对象的group配置是否和传入的group匹配
    - 2.2.若上一步匹配,则通过getExtension(name)获得拓展对象
    - 2.3.判断传入的names是否匹配,isActive(activate, url)通过activate.value值判断是否激活
    - 2.4.若上面条件都满足,添加到list里,并通过ActivateComparator给activate列表排序
        - ActivateComparator.compare()排序逻辑
        - 1.获取比较的两个类上的Activate注解,通过before或after属性进行排序
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlyingZCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值