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。
package com.ruubypay.miss.api;
public interface SayHelloApi {
String sayHello(String name);
}
SayHelloServiceImpl,主要是对API的一个实现。
package com.ruubypay.miss.extension;
import com.ruubypay.miss.api.SayHelloApi;
public class SayHelloServiceImpl implements SayHelloApi{
@Override
public String sayHello(String name) {
return "hello,"+name;
}
}
实现SPI的关键就是,
1)要在类根路径下创建META-INF文件夹,并在META-INF文件夹下创建services文件夹。
2)创建要加载的扩展接口的接口全类路径名文件。文件内容是所有实现类的全类路径名称,按行分割,#号行为注释行
文件内容为:
TestMain,则为SPI动态发现实现类的代码:
package com.ruubypay.miss;
import com.ruubypay.miss.api.SayHelloApi;
import java.util.ServiceLoader;
public class TestMain {
public static void main(String[] args) {
ServiceLoader<SayHelloApi> serviceLoader = ServiceLoader.load(SayHelloApi.class);
serviceLoader.forEach(it->{
System.out.println("发现的实现类为:"+it.getClass().getName()+
",调用方法结果为:"+it.sayHello("ruubypay"));
});
}
}
演示结果:
以上,即为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扩展加载是如何约定的,其实是在ExtensionLoader的源码里有说明,我这里加上了自己的注释。
/**
* 兼容jdk的SPI的加载路径
*/
private static final String SERVICES_DIRECTORY
= "META-INF/services/";
/**
* dubbo的SPI的路径
*/
private static final String DUBBO_DIRECTORY
= "META-INF/dubbo/";
/**
* dubbo内部扩展的路径。即META-INF/dubbo/internal/
*/
private static final String DUBBO_INTERNAL_DIRECTORY
= DUBBO_DIRECTORY + "internal/";
dubbo有很多内置的扩展点实现。不如我们看看:
看一看Complier有哪些扩展:
很明显,生成代码编译方式Dubbo内置支持了jdk和javassist两种方式
Dubbo的SPI扩展点贯穿了整个工程,拿filter拦截器模块举例:
很多的扩展发现基本都是依赖于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,先要弄清楚下面的一些概念:(由于上图的链接已有详细解释,下面仅仅是简单介绍。)
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。
2)标有Adaptive注解的函数表示其需要进行扩展点自适应,根据url对象表示到底使用哪个扩展。
3)标有Activate注解的扩展表示其需要自动激活。
4)扩展点加载API
在dubbo里,到处可见如下代码,表示根据名称获得扩展对象:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
在dubbo里,到处可见如下代码,表示获得自适应扩展对象:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()
在dubbo里,到处可见如下代码,表示获取符合条件的自动激活的扩展对象集合:
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
总结
本文并未详细解释每一行源码,但已经分析了Dubbo的SPI扩展点源码实现的基本概念和类图。相信再看细节也不会是难事。
(完)
chen陈序猿|一起学技术吧
以上是关于Dubbo底层源码分析之SPI扩展点的主要内容,如果未能解决你的问题,请参考以下文章