Dubbo原理<一> Dubbo SPI详解
发布日期:2022-02-26 14:49:43 浏览次数:44 分类:技术文章

本文共 5379 字,大约阅读时间需要 17 分钟。

文章目录


前言

Dubbo源码里面很多基于Dubbo SPI进行扩展类的管理,所以我们先从Dubbo SPI开始讲解


一、SPI是什么?

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类;

二、JAVA SPI

默认加载 META-INF/services/接口全限定名 内容为实现类的全限定的类名; 以换行符分隔多个实现类

ServiceLoader
serviceLoader = ServiceLoader.load(Worker.class); Iterator
workerIterator = serviceLoader.iterator();if(workerIterator.hashNext()){
//执行到这就会加载META-INF/services/package.Worker文件并获取文件内容的实现类名称 Worker robot= workerIterator.next();,//实例化返回}

缺点:因为不知道实现类是哪个、所以会遍历全部实现类并实例化

三、DUBBO SPI:

扩展了java spi的功能, 默认加载 META-INF/services/接口全限定名 || META-INF/dubbo/接口全限定名 || META-INF/dubbo/internal/接口全限定名

实现了服务发现、自适应、自动激活、IOC、AOP功能;

1、配置介绍:

在这里插入图片描述

2、注解作用和规则介绍:

1、@SPI

   标记在接口表示参与dubbo spi功能,没有抛出异常;@SPI(value="")可以指定接口默认实现

操作方法

ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("hessian") 	//从配置文件,获取指定别名的接口实现类,获取不到则抛异常   ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension()		//从配置文件,获取@SPI注解value指定的接口实现类,value=空,则返回null

获取过的对象实例都会被缓存起来,所以也算一个bean容器

2、@Adaptive

   一般标记在接口方法上,并且被标记的方法参数必须要有dubbo的URL类型否则报错;

   @Adaptive(“type”) 表示根据方法参数URL对象中的type来寻找实现类;
   如果标记在实现类上表示它就是一个自适应实现类;
操作方法

ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension().doExport(URL.valueOf("http://www.baidu.com?type=hessian"), null);// 获取自适应类的实现,并调用其方法doExport,然后获取type的值hessian,作为实现类;

一般自适应类都是系统运行期生成,代码如:

package kd.bos.debug.mservice;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Protocol$Adpative implements kd.bos.debug.mservice.Protocol {
public void doExport(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.rpc.Invocation arg1) {
if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; if (arg1 == null) throw new IllegalArgumentException("invocation == null"); String methodName = arg1.getMethodName(); String extName = url.getMethodParameter(methodName, "type", "default");//type为@Adaptive的value,为空则默认为接口名如protocol;这个default是@spi("default")获取,即默认扩展类 //如果@Adaptive的value=protocol则会翻译成 String extName= url.getProtocol()==null?"defualt":url.getProtocol(); if(extName == null) throw new IllegalStateException("Fail to get extension(kd.bos.debug.mservice.Protocol) name from url(" + url.toString() + ") use keys([type])"); kd.bos.debug.mservice.Protocol extension = (kd.bos.debug.mservice.Protocol)ExtensionLoader.getExtensionLoader(kd.bos.debug.mservice.Protocol.class).getExtension(extName); //根据url中的值,获取实现类 extension.doExport(arg0, arg1);}}

3、@Activate

   一般标记在实现类上 @Activate(value={“provider”},group={“v1”}); 表示根据url中存在key=provider,且指定的group也一样 则是一个激活的实现类;

操作方法

List
activeProtocolImpls=extensionLoader.getActivateExtension(URL.valueOf("http://www.baidu.com?provider=hessian"),"provider","v1"); // 1、获取存在@Activate注解的实现类,且满足注解的value在url的key中能对应,且group为v1的实现类 // 2、获取别名为hession的实现类

4、IOC功能

默认在找到扩展类实现类实例化后,会调用其set方法从springIOC、dubbo spi中给其自动注入成员对象值,即IOC操作	默认dubbo spi 工厂 自动注入的成员对象是一个自适应类;所以接口的方法上必须有@Adaptive注解,才会自动注入成功

5、@DisableInject

标记在实现类的setxx方法上,表示不自动注入,即不会调用实现类的setxx方法给其自动注入值;

6、wrapper包装类

规定interface的实现类,如果有interfaceImpl(Interface interface)这种自持有的构造函数则被认为是包装类;spi获取的时候会返回这个包装类,并把真实的接口实现类注入到Wrapper;包装类就是装饰器模式,可以在实现类调用前后做操作;相当于AOP有多个wrapper类,则是wrapper1(instance) 注入到wrapper2(wrapper1)中;返回wrapper2这样;即多个wrapper构成责任链模式
//包装类构造方法案例:public class XxxProtocolWrapper implements Protocol {
Protocol impl; public XxxProtocolWrapper(Protocol protocol) {
impl = protocol; } }

3、实战使用:

1、配置

在这里插入图片描述
2、接口
在这里插入图片描述
3、实现类
在这里插入图片描述在这里插入图片描述
4、包装类
在这里插入图片描述
5、自动激活类
在这里插入图片描述
在这里插入图片描述

6、测试IOC自动注入类

在这里插入图片描述
7、测试类

public static void main(String[] str){
ExtensionLoader
extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class); extensionLoader.getExtension("hessian").doExport(URL.valueOf("http://www.baidu.com?type=dubbo"),null); extensionLoader.getExtension("dubbo").doExport(URL.valueOf("http://www.baidu.com?type=dubbo"),null); System.out.println(extensionLoader.getDefaultExtension()); List
activeProtocolImpls=extensionLoader.getActivateExtension(URL.valueOf("http://www.baidu.com?provider=hessian"),"provider","v1"); for(Protocol protocol:activeProtocolImpls){
protocol.doExport(URL.valueOf("http://www.baidu.com?type=hessian"),null); } extensionLoader.getExtension("auto").doExport(URL.valueOf("http://www.baidu.com?type=dubbo"),null); extensionLoader.getAdaptiveExtension().doExport(URL.valueOf("http://www.baidu.com?type=hessian"), new Invocation() {
...});}

8、运行结果:

do export before

HessianProtocol
do export after
do export before
DubboProtocol
do export after
null
do export before
A activateProtocol
do export after
do export before
HessianProtocol
do export after
do export before
the auto inject bean is [kd.bos.debug.mservice.Protocol$Adpative]
do export after
do export before
HessianProtocol
do export after

4、源码分析

1、获取扩展类加载对象 : ExtensionLoader.getExtensionLoader(Protocol.class):

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、获取实现类 extensionLoader.getExtension(“hessian”)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时经过loadFile,已经把配置文件中的 普通实现类有@Activate注解的实现类有@Adaptive注解的实现类满足wrapper结果的包装类 都给加载出来了;
现在我们再看上面createExtension方法里面的 自动注入 injectExtension方法 (高版本的dubbo会在此方法判断,方法没有@DisableInject注解才自动注入)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3、获取自适应类*extensionLoader.getAdaptiveExtension().doExport(url)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、获取激活类extensionLoader.getActivateExtension(url,“type”,“group”)

在这里插入图片描述

在这里插入图片描述


后言

Dubbo SPI的功能讲完了,接下来会讲解一般项目集成dubbo的入口在哪,原理是什么,以及dubbo常用参数的配置详情,zk存储节点的介绍;

转载地址:https://blog.csdn.net/qq_32129371/article/details/114908286 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:BMC REDFISH
下一篇:dubbo原理<二> 流程入口+ 参数配置

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月06日 04时58分47秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章