一.目的:
使用者能夠不改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。