一.目的:
使用者能够不改dubbo项目的情况下,在应用项目中增加相应类及配置就可以实现扩展。
二.类似jdk的spi,但比jdk spi有以下优势:
1.jdk的spi默认会加载并实例化所有扩展点类,如果对应扩展点没用到且实例化消耗大,则是个耗资源的点。
2.不支持依赖注入,即如果在扩展点类a中,有b扩展点属性实例,则在加载扩展点类a时不会自动注入b。
3.加载扩展点失败会吞掉异常,比如加载一个扩展类a,如果其所依赖的jar找不到,不会报这个错误,反而会报不支持。
三.实现原理:
有个核心的ExtensiontLoader用于加载扩展点。
dubbo规定,protocal, filter, transport等都存在独立的ExtensionLoader。
注入使用的时候,对应扩展点类是织入相应字节码的类,主要是为了实现运行时动态根据调用的url相应参数去查找具体的扩展点实现类。
protocal实现类:
会生成以下类:
class Protocol$Adpative implements Protocol{
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException{
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();
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)com.alibaba.dubbo.common.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 com.alibaba.dubbo.rpc.RpcException{
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)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
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!");
}
}
这里可以看到,主要是实现了根据url的protocal属性去查找对应的扩展点实现类。
默认实现类优先取Adaptive标注上对应key的扩展点类。取不到则取spi标注上对应类。
四.使用示例:
增加一个dubbo方法执行时间log。
这里通过增加一个filter来解决,dubbo提供了fiter扩展点。
需要进行以下步骤:
1.创建filter类
这里是:XxxFilter
2.建立以下目录及文件
这里的META-INF, dubbo及 com.alibaba.dubbo.rpc.Filter文件,里面有对应内容:
xxx=com.xxx.XxxFilter
注:这里的xxx在用到的地方引用即可。比如在服务提供方上加入: <dubbo:provider filter="xxx"/>
然后扩展点加载类就会加载【com.alibaba.xxx.XxxFilter】
|
3.在使用的地方引用上述的【xxx】
在服务提供方上加入: <dubbo:provider filter="xxx"/>,这里的xxx是对应扩展点目录文件内容中对应key值。
然后扩展点加载类就会加载【com.alibaba.xxx.XxxFilter】
五.dubbo filter原理
dubbo在暴露服务时,会对invoker做一层封装,使得filter能够正常执行。
比如存在:a,b,c三个filter,要执行的inovker是target。
则会形成链表:a->b->c->target
最终这个target对应服务被调用时,会依次执行a,b,c,target。