Dubbo源码分析:URL总线和ExtensionLoader-框架基石

版权声明:非商业目的可自由转载,转载请标明出处 https://blog.csdn.net/u010013573/article/details/84637383

概述

ExtensionLoader类为Dubbo框架SPI的实现类,相当于JDK的ServiceLoader,也实现了Spring的IOC功能,通过ExtensionLoader配合URL完成对应类实现的加载,是Dubbo框架高度可拓展性实现的基础。以下各个规则和实现都是在ExtensionLoader提供实现,如从META-INF目录获取SPI的类声明,Adaptive注解配合URL参数或protocol,自适应获取SPI接口实现类等。
在这里插入图片描述

自定义SPI

  1. 所有需要通过ExtensionLoader解析、设置属性,自适应获取实现类的接口,均需要增加@SPI声明,如下为:Protocol接口:
    在这里插入图片描述
  2. 增加了根据实例别名的概念,如:
    dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol,默认java spi则只在META-INF/services目录,包含接口全名作为文件,文件内容为各个实现类全名列表;
  3. 增加存放spi定义文件的目录,包括:META-INF/dubbo,META-INF/dubbo/internal和META-INF/services,而不只是MATE-INF/services。
    1,2 如图:
    自定义spi

Adaptive与Activate注解

  • @Adaptive为根据URL参数,在运行时动态决定选择哪种接口实现,@Adaptive注册可以用在实现类上,但是只能放在接口的其中一个实现类上。更多的时候是将@Adaptive注解放在接口方法上,表示这些方法可以在运行时决定使用哪个spi实现类。如下为注册工厂的接口:
    registryFactory
    在getRegistry方法上使用@Adaptive({“protocol”})表示:根据URL的protocol参数的值,如:zookeeper://xxx,决定使用以下哪个注册工厂实现:
    在这里插入图片描述
    动态选择接口实现的核心源码如下:
    在这里插入图片描述
    如上图:为从method获取Adaptive注解,然后如下图:获取@Adaptive注解的value数组,如果没有则根据接口名设置value。
    在这里插入图片描述
    如下图:

  • 根据value的值,决定是从url的parameter获取spi实现,还是根据URL的protocol选择spi实现。即:如果value值为protocol则根据url的protocol,否则根据url中parameter选择。

  • 通过value产生了getNameCode的具体值,根据getNameCode得出了当前选择的spi实现类的别名extName,最后通过getExtensionLoader(接口.class).getExtension(extName)加载最终调用的实现类。
    在这里插入图片描述

  • Activate为根据URL参数,动态决定是否激活某个类,如ProtocolFilter接口的实现类就是根据Activate规则,决定激活哪些过滤器。

Wrapper包装器

  • 定义:某个接口的包装类,实现该接口并且只包含一个以该接口作为唯一参数的构造函数的类
  • 作用:用于增强、装饰该接口实际提供功能的类的实例,即该接口的具体实现,多个包装器类无序地包装到该实例上。如Protocol接口包括两个wrapper类分别为:
    在这里插入图片描述

URL总线

dubbo为以URL为主线的一个框架,即根据URL中的相关参数,adaptive自适应选择对应的spi实现类,核心实现为:
getAdaptiveExtensionClass
在这里插入图片描述
在这里插入图片描述
基本逻辑为:
根据构造函数传入的type:
在这里插入图片描述
依次查看type的每个方法的每个参数,如果存在参数类型为URL则就找到了;如果没有,则继续查看参数所属类的每个方法,是否存在“以get开头,不存在参数,返回类型为URL”的方法,有则找到了。

案例:ServiceConfig服务注册

在dubbo-bootstrap中,遍历ServiceConfig列表,开始提供者的服务导出注册。
ServiceConfig的export导出提供者的方法到注册中心,从源码实现来看是通过ExtensionLoader以下声明了一个protocol:
在这里插入图片描述
实际调用的是RegistryProtocol,而能判断最终调用的是RegistryProtocol,是因为url为:registry://xxx,具体为invoker的url为registry://xxx,即url.getProtocol()返回为registry。
Protocol接口为方法export(Invoker invokder)对应的Invoker接口继承于Node,Node接口包含URL getUrl()方法。
在这里插入图片描述
dubbo生成的adative类:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
	public void destroy() {
		throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	public int getDefaultPort() {
		throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
		com.alibaba.dubbo.common.URL url = arg0.getUrl();
		// 此处url.getProtocol不为null,返回registry
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.export(arg0);
	}
	public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
		if (arg1 == null) 
			throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}
}

参考:
https://blog.csdn.net/yangxiaobo118/article/details/80696830

猜你喜欢

转载自blog.csdn.net/u010013573/article/details/84637383