擴展點能力
- 能load class,這個class除了頂層接口class(在ExtensionLoader中對應type字段),還能load各實現類的class。
- 能創建instance。
- 能指定這個頂層接口的默認實現類的beanName。做法參見SPI注解部分。
- 能把創建出來的instance的字段注入。set開頭的且有一個參數且是public的,注入。
- 能adaptive。根據url上對該接口配置的實現類,將該接口的事情交給這個實現類去做(我更多的理解成委托)。此能力采用代碼生成再編譯的方式。代碼生成示例可以參見adaptive類代碼示例。 adaptive只會生成一個adaptive實現類。生成代碼的邏輯在com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtensionClassCode()
- 能wrapper。wrapper是指這個頂層接口的實現類的構造函數的入參是這個頂層接口類型。那么這個實現類稱之為wrapper類,可以有多個,構建instance時不分多個之間的順序。因為用來給構造函數傳參的instance是這個頂層類的默認實現。比如com.alibaba.dubbo.rpc.Protocol接口,有實現類 com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol beanName是injvm,QosProtocolWrapper、ProtocolListenerWrapper、ProtocolFilterWrapper是其三個wrapper類。創建warpper instance的代碼邏輯在com.alibaba.dubbo.common.extension.ExtensionLoader.createExtension(String)中。
- 能active。實現類加了Activate注解的。在ExtensionLoader.getActivateExtension時會根據當前的url(配置信息)中值來匹配Activate注解中指定的值是否能match,能match的表示是activate,意思是命中的。在過濾器擴展點中用到。比如這個過濾器是給CONSUMER group用。示例有ExceptionFilter等。同時該注解還能支持order屬性來定義bean的順序。
擴展點使用
配置文件相關
配置文件放在哪里?
- META-INF/dubbo/internal/配置文件
- META-INF/dubbo/配置文件
- META-INF/services/配置文件
配置文件名是頂層接口全限定名,比如:com.alibaba.dubbo.rpc.Protocol
配置文件中內容:
一行是一個實現類的定義,大致是
beanName=實現類的class的全限定名,這個后面還可以接上#xxx(這個能力實際使用少)。beanName=這部分不是必須的。可以僅僅寫實現類的全限定名。
注解相關
SPI
SPI注解用在頂層接口上,其值表示這個接口的默認實現類的beanName,也即是說指定一個頂層接口的默認實現通過SPI注解加載頂層接口上指定即可。
Adaptive
Adaptive注解用在頂層接口或者接口的方法上,其表示這個接口或者這個方法需要有adaptive的類委托完成,未加注解的會生成不支持的操作的方式實現。
Activate
Activate注解用在實現類上,其表示實現類在rpc時根據url參數中以及注解中指定的key 目標value是否能匹配來決定此bean是否被選中(一般用在過濾器的命中判斷上)。
生成的代碼
adaptive類代碼示例
package com.alibaba.dubbo.registry;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class RegistryFactory$Adaptive implements com.alibaba.dubbo.registry.RegistryFactory {
public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString()
+ ") use keys([protocol])");
com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory) ExtensionLoader
.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
return extension.getRegistry(arg0);
}
}
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Cluster$Adaptive implements com.alibaba.dubbo.rpc.cluster.Cluster {
public com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory arg0)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("cluster", "failover");
if (extName == null)
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url(" + url.toString()
+ ") use keys([cluster])");
com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster) ExtensionLoader
.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
return extension.join(arg0);
}
}
雜項
查找所有的dubbo擴展點形式的配置文件
find ./ -type f -name "com.alibaba.dubbo*"|grep -v "/target/"|grep -v "/bin/"|grep -v "/test/"
另:
關於代理模式,dubbo未實現通用的,只是rpc語義實現里rpc調用的代理,借助擴展點機器加動態代理完成。
具體其頂層接口是com.alibaba.dubbo.rpc.ProxyFactory。用在比如將EchoService編織進每次RPC調用中。
