Dubbo采用的是内核+扩展
的体系结构,除了Service和Config层,其他层的功能都是可扩展的(Proxy、Registry、Cluster、Monitor、Protocol、Exchange、Transport、Serialize)。
也就是说,这些层的功能模块,都可以通过配置的方式灵活地切换实现,并不需要修改框架的代码。
Dubbo 使用 URL 总线模式(包含了Key-Value)传递配置信息,所有的状态数据信息都可以从URL中解析获取。
Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
Dubbo本身也为各层提供了多种实现,比如Registry,Dubbo就提供了MulticastRegistry、RedisRegistry、ZookeeperRegistry等实现。(使用Redis作为注册中心)。
Dubbo同样支持用户自定义的扩展实现。
Dubbo的扩展点由Java的SPI扩展点发现机制扩展而来。
自定义的扩展方式可以支持延迟加载,不用每次都一次性加载出所有的配置功能;Dubbo扩展还支持以key=value的方式进行配置;Dubbo扩展还支持轻量级的依赖注入,会将扩展点实现类中的另外的扩展点引用“顺便”给初始化了。
getExtensionLoader
方法,用来获取某个接口的ExtensionLoader
,每个接口只有一个该对象。 activeExtension
adaptiveExtension
defaultExtension
@Adaptive
注解可以可以用来标注在某个接口的实现类上,表示这个实现并不是用来做业务逻辑处理的,而是用来适配这个接口的各种实现的。
比如:AdaptiveExtensionFactory
被标注了@Adaptive
,在调用ExtensionLoader
的
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
方法时,将返回
AdaptiveExtensionFactory
实例,它用来适配SpiExtensionFactory
和SpringExtensionFactory
实现,具体用哪个,会根据运行时的状态来确定。
@Adaptive
注解同样可以标注在接口的方法上。ExtensionLoader通过分析接口配置的adaptvie规则来动态生成adaptvie类。
@Adaptive
有一个value属性,通过这个值来设置规则。由于Dubbo采取了URL总线的方式来传递数据,所以加上该注解的方法参数,需要可以获得URL
对象,或者直接就是URL
类型或者子类型的对象。以value的值为key,去URL
里面查找。
如果查找不到,则会把类名拆分,例如:com.alibaba.dubbo.xxx.YyyInvokerWrapper将会拆分成String[] {“yyy.invoker.wrapper”},用于在URL中查找配置。
如果不能获取到,则会返回SPI
标注的接口中声明的名称的扩展。
@Adaptive接口标注的实现,是一个适配器
2、另一种情况是Dubbo框架动态生成适配器类
如果没有找到@SPI接口的@Adaptive实现,ExtensionLoader会动态创建适配器类。
ExtensionLoader通过分析接口配置的adaptive规则,动态生成类,并且加载到ClassLoader中。
该注解有一个value属性,通过这个属性,可以设置该接口的adaptive规则,所以结合URL总线设计,参数都需要从URL中获取。
所以这样的方法都需要提供URL对象作为参数。
以Transporter类为例:
@SPI("netty")
public interface Transporter {
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
这个接口是一个扩展接口,默认扩展名为`netty`。它有两个方法, 一个是bind,提供URL参数,注解说明,它是配置服务端的transporter功能; 一个是connect,提供URL参数,注解说明,它是配置客户端的transporter功能; 它没有Adaptive实现,但是它的方法提供了URL参数。 ![](http://owu0nfc62.bkt.clouddn.com/transporter.png) `ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();` 的时候,按如下调用链 `getAdaptiveExtension` -> `createAdaptiveExtension` -> `getAdaptiveExtensionClass` -> `createAdaptiveExtensionClass` -> `createAdaptiveExtensionClassCode` `createAdaptiveExtensionClassCode`方法,将会构造一个临时的Adaptive类文件(当然,它需要实现扩展接口,例如Transporter),名字为`接口名+$Adaptive` 关于接口方法的实现,它将从URL中获取接口方法注解上的参数,Transporter接口中,如:Constants.SERVER_KEY, Constants.TRANSPORTER_KEY。 用于构造Transporter实现,并且最终生成如下的代码:
package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
这样一来,除了参数获取方式不一样。其实和自己定义的Adaptive实现没什么区别了。 **拿到Adaptive(其实这就是一个代理工厂),并在代理工厂内屏蔽实现的差异(获取到准确的扩展实现,然后调用方法,最后返回)** 扩展灵活配置,还体现在另一个注解上`@Activate`。这个注解使用在接口的实现类上,用来标注使用这个实现的前提条件。 比如:`ValidationFilter`被标注为:
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {...}
表明这个Filter实现,在做Validation的时候,可以在客户端和服务端生效; value表示另外一个激活条件,注意上面截图的方法,active extension相关的方法,需要提供一个URL参数, 那么这个value配置的值,表示URL中必须要有指定的参数才能激活这个扩展。 order值越大越靠前。 `这里指的是对 框架本身对接口的实现 的排序,用户扩展的实现,将放置在后面。` Dubbo中对该注解用得最多的地方是`Filter`的实现。Dubbo的调用经常会使用过滤器链的形式,哪些实现以及实现的顺序,是由`@Activate`注解来控制的。
@SPI("default")
public interface MyExtension {
@Adaptive
String sayHello(String name, String extensionType);
}
它的方法没有提供URL,所以,只能显式定义一个Adaptive类。
2、定义两个实现:
public class DefaultExtension implements MyExtension {
@Override
public String sayHello(String name, String extensionType) {
return "This is DEFAULT implementation, and hello - "+name;
}
}
public class SWExtension implements MyExtension {
@Override
public String sayHello(String name, String extensionType) {
return "This is SW implementation, and hello - "+name;
}
}
3、定义Adaptive类,根据类型获取实现。
@Adaptive
public class AdaptiveExtension implements MyExtension {
public String sayHello(String name, String extensionType) {
ExtensionLoader loader = ExtensionLoader.getExtensionLoader(MyExtension.class);
MyExtension extension = loader.getDefaultExtension();
switch (extensionType){
case "default":
extension = loader.getExtension("default");
break;
case "sw":
extension = loader.getExtension("sw");
break;
default:
break;
}
return extension.sayHello(name,extensionType);
}
}
4、配置META-INF/dubbo/cn.irving.extension.MyExtension
default=cn.irving.extension.DefaultExtension
sw=cn.irving.extension.SWExtension
adaptive=cn.irving.extension.AdaptiveExtension
5、定义测试类
public class ExtensionTest {
public static void main(String[] args) {
MyExtension extension = ExtensionLoader.getExtensionLoader(MyExtension.class).getAdaptiveExtension();
System.out.println(extension.sayHello("Irving","sw"));
// ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
}
运行结果:
This is SW implementation, and hello - Irving
参考资料:
http://dubbo.io/books/dubbo-dev-book/SPI.html
https://my.oschina.net/bieber/blog/418949
-EOF-