前言
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存储节点的介绍;