1.JDK内置SPI机制

1.1 概述

       SPI 全称为 Service Provider Interface),是JDK内置的一种服务提供发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

1.2 约定

  • 在META-INF/services/目录中创建以Service接口全限定名命名的文件,该文件内容为Service接口具体实现类的全限定名,文件编码必须为UTF-8。
  • 使用ServiceLoader.load(Class class); 动态加载Service接口的实现类。
  • 如SPI的实现类为jar,则需要将其放在当前程序的classpath下。 Service的具体实现类必须有一个不带参数的构造方法

1.3 示例

接口:SPIDemoService
dubbo扩展机制SPI(一)

实现类1:ChineseSPIDemoServiceImpl
dubbo扩展机制SPI(一)
实现类2:EnglishSPIDemoServiceImpl
dubbo扩展机制SPI(一)
dubbo扩展机制SPI(一)
dubbo扩展机制SPI(一)
      至于jdk是咋么实现的,读者可跟踪ServiceLoader.load源码,看看里面是怎么实现的。本博文重点在于DubboSPI机制,所以此处就不再说明jdkSPI源码实现。

2.Dubbo SPI机制

Dubbo 改进了 JDK 标准的 SPI 的以下问题

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName()
    获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持ruby,而不是真正失败的原因。
  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

2.2 示例

接口:DubboSPIDemoService
dubbo扩展机制SPI(一)
接口实现一
dubbo扩展机制SPI(一)
接口实现二:
dubbo扩展机制SPI(一)
配置:
dubbo扩展机制SPI(一)
测试结果:
dubbo扩展机制SPI(一)

2.3 adaptive注解-dubboSPI自适应扩展

1.Fruit接口
dubbo扩展机制SPI(一)
接口实现一
dubbo扩展机制SPI(一)
接口实现二

dubbo扩展机制SPI(一)
测试结果
dubbo扩展机制SPI(一)
总结:
.      ExtensionLoader.getExtensionLoader(Fruit.class).getAdaptiveExtension() 相当于调用Fruit$Adaptive类的相关方法,然后此类会根据url所带的参数具体解析那个服务。如果参数没有的话,则使用默认值方法必须要有url(这也是dubbo很重要的一个组成)类型的入参或者入参有返回类型为url的方法。

2.4 ExtensionLoader#getAdaptiveExtension()源码跟踪

     如下图所示,为getAdaptiveExtension()方法实现,分析可得,该方法的核心主要是createAdaptiveExtension()方法,
dubbo扩展机制SPI(一)
      继续往下跟踪,可以发现,dubbo主要是通过AdaptiveClassCodeGenerator来为目标类生成子类代码,并以字符串的形式返回,然后利用dubbo的动态编译技术生成成目标接口的子类的。如下图就是读者在调试dubbo源码的过程生成子类。
dubbo扩展机制SPI(一)
     对于dubbo的SPI机制可以说也是dubbo很重要的一大特性,许多内部特性都是基于它来实现的,如协议扩展,注册中心扩展等,对于要阅读dubbo源码的童鞋来说,SPI可以算是最基本的一个知识点;限于篇幅,本博文对ExtensionLoader源码只是做简要分析,其实内部还有很多知识点可以剖析的,如包装类是怎么跟@adaptive注解相关联的,后续博主也会找时间写另一篇博客进行专门解析ExtensionLoader源码,如有兴趣的童鞋也可以在下面留言。

相关文章: