Dubbo原理<一> Dubbo SPI详解


前言

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


一、SPI是什么?

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

二、JAVA SPI

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

ServiceLoader<Worker> serviceLoader = ServiceLoader.load(Worker.class);
 Iterator<Worker> 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<Protocol> 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<Protocol> 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<Protocol> 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存储节点的介绍;

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值