Dubbo底层源码分析之SPI扩展点

Posted chen陈序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo底层源码分析之SPI扩展点相关的知识,希望对你有一定的参考价值。

之前一段时间一直在撸Dubbo的原理与源码实现,本文笔者简单说明一下何为SPI,以及总结一下在dubbo中是如何通过SPI扩展点进行接口扩展实现类的加载。



什么是SPI

想要理解Dubbo的SPI扩展点机制,先要理解什么是SPI


SPI,称即:Service Provider Interface,是一种服务提供的自动发现机制。JDK本身也有对SPI的支持,我们先写一个JDK版本的SPI扩展点Demo。

JDK中的SPI扩展点实现

首先,演示工程的结构如下:

SayHelloApi,主要声明一个API。

 
   
   
 
  1. package com.ruubypay.miss.api;

  2. public interface SayHelloApi {

  3.    String sayHello(String name);

  4. }


SayHelloServiceImpl,主要是对API的一个实现。

 
   
   
 
  1. package com.ruubypay.miss.extension;

  2. import com.ruubypay.miss.api.SayHelloApi;

  3. public class SayHelloServiceImpl implements SayHelloApi{

  4.    @Override

  5.    public String sayHello(String name) {

  6.        return "hello,"+name;

  7.    }

  8. }


实现SPI的关键就是,

1)要在类根路径下创建META-INF文件夹,并在META-INF文件夹下创建services文件夹。

2)创建要加载的扩展接口的接口全类路径名文件。文件内容是所有实现类的全类路径名称,按行分割,#号行为注释行

Dubbo底层源码分析之SPI扩展点

文件内容为:

Dubbo底层源码分析之SPI扩展点

TestMain,则为SPI动态发现实现类的代码:

 
   
   
 
  1. package com.ruubypay.miss;

  2. import com.ruubypay.miss.api.SayHelloApi;

  3. import java.util.ServiceLoader;

  4. public class TestMain {

  5.    public static void main(String[] args) {

  6.        ServiceLoader<SayHelloApi> serviceLoader = ServiceLoader.load(SayHelloApi.class);

  7.        serviceLoader.forEach(it->{

  8.            System.out.println("发现的实现类为:"+it.getClass().getName()+

  9.                    ",调用方法结果为:"+it.sayHello("ruubypay"));

  10.        });

  11.    }

  12. }


演示结果:

Dubbo底层源码分析之SPI扩展点


以上,即为JDK的SPI扩展点发现机制。会默认去寻找META-INF/services文件夹去寻找对应的接口文件,然后按照内容去自行加载扩展点实现类。

Dubbo的SPI

Dubbo的SPI和JDK的SPI不一样,Dubbo的SPI扩展点机制是自己实现的一套SPI,对JDK的SPI来说,是一种功能上的增强。


Dubbo的精华源码之SPI扩展点实现机制,通通都在dubbo-common 模块的extension包下,它是Dubbo SPI的核心 。


Dubbo底层源码分析之SPI扩展点


先看一下Dubbo的SPI扩展加载是如何约定的,其实是在ExtensionLoader的源码里有说明,我这里加上了自己的注释。


 
   
   
 
  1.   /**

  2.     * 兼容jdk的SPI的加载路径

  3.     */

  4.    private static final String SERVICES_DIRECTORY

  5.    = "META-INF/services/";

  6.    /**

  7.     * dubbo的SPI的路径

  8.     */

  9.    private static final String DUBBO_DIRECTORY

  10.    = "META-INF/dubbo/";

  11.    /**

  12.     * dubbo内部扩展的路径。即META-INF/dubbo/internal/

  13.     */

  14.    private static final String DUBBO_INTERNAL_DIRECTORY

  15.    = DUBBO_DIRECTORY + "internal/";


dubbo有很多内置的扩展点实现。不如我们看看:

Dubbo底层源码分析之SPI扩展点

看一看Complier有哪些扩展:

Dubbo底层源码分析之SPI扩展点

很明显,生成代码编译方式Dubbo内置支持了jdk和javassist两种方式


Dubbo的SPI扩展点贯穿了整个工程,拿filter拦截器模块举例:


Dubbo底层源码分析之SPI扩展点


很多的扩展发现基本都是依赖于SPI自动发现实现类。


由于篇幅有限,本文不会详细解释SPI扩展点的源码。但会说明每一个类的职责,大概可以总结成如下图的方式:


 |--factory

 |     |--AdaptiveExtensionFactory   #自适应扩展实现类

 |     |--SpiExtensionFactory        #SPI扩展实现类

 |

 |--support

 |     |--ActivateComparator #自动激活的扩展点的一个排序器

 |

 |--Activate  #自动激活加载扩展的注解

 |--Adaptive  #自适应扩展点的注解

 |--ExtensionFactory  #扩展点对象生成工厂接口,向扩展对象中注入依赖属性

 |--ExtensionLoader   #扩展点核心逻辑实现类

 |--SPI   #扩展点注解,标有该注解的接口使用扩展点的方式加载实现类。


看到这里有点蒙,没关系,可以接着往下看。

Dubbo为何要自己实现SPI

实际上官方文档已经说的很清楚了。大家从官方文档可以查看:

https://dubbo.gitbooks.io/dubbo-dev-book/SPI.html

这里摘录部分:

Dubbo底层源码分析之SPI扩展点


在上面,有很多名词,比如自适应,自动激活等。这些名词和Dubbo的SPI的一些概念有关,想要弄清楚Dubbo的SPI,先要弄清楚下面的一些概念:(由于上图的链接已有详细解释,下面仅仅是简单介绍。)


1)扩展点的自动包装


自动加载包装扩展点的类,例如,Protocol的扩展点包装类为ProtocolWapper,在包装类里实现部分类似于Spring AOP的逻辑。


2)扩展点自动装配


A扩展点依赖B扩展点,则自动在A扩展点里注入B的扩展点。


3)扩展点自适应


在2)中,如果B的扩展点有多个,如何知道到底该注入哪一个扩展点。这就是扩展点的自适应。在Dubbo中,依靠URL对象(可以理解为我们的配置对象)来实现,仅当调用扩展点方法时,才动态的判断到底需要哪个实现类,然后调用其实现。


4)扩展点自动激活


有些扩展可以同时加载多个扩展点实现,如Filter,如果扩展点的实现类上有@Activate注解,表明该扩展实现类需要自动激活。(立即加载)

Dubbo的SPI在其源码里的应用展示

这里展示一些Dubbo应用其SPI的方式:


1)标有SPI注解的接口表示其使用SPI加载扩展,属性表示其默认的实现类,比如代理工厂扩展,我们可以发现其默认的实现为javassist。


Dubbo底层源码分析之SPI扩展点


2)标有Adaptive注解的函数表示其需要进行扩展点自适应,根据url对象表示到底使用哪个扩展。


Dubbo底层源码分析之SPI扩展点


3)标有Activate注解的扩展表示其需要自动激活。



4)扩展点加载API


在dubbo里,到处可见如下代码,表示根据名称获得扩展对象:

 
   
   
 
  1. ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)


 

在dubbo里,到处可见如下代码,表示获得自适应扩展对象:

 
   
   
 
  1. ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()


在dubbo里,到处可见如下代码,表示获取符合条件的自动激活的扩展对象集合:

 
   
   
 
  1. List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

总结

本文并未详细解释每一行源码,但已经分析了Dubbo的SPI扩展点源码实现的基本概念和类图。相信再看细节也不会是难事。

(完)

chen陈序猿|一起学技术吧

微信ID:echo_陈

以上是关于Dubbo底层源码分析之SPI扩展点的主要内容,如果未能解决你的问题,请参考以下文章

dubbo源码分析之基于SPI的强大扩展

02.dubbo源码解析之Dubbo扩展点加载

dubbo源码分析之过滤器Filter-12

源码分析---SOFARPC可扩展的机制SPI

dubbo源码解析-spi

dubbo源码阅读之SPI