Dubbo原始碼分析:URL匯流排和ExtensionLoader-框架基石
概述
ExtensionLoader類為Dubbo框架SPI的實現類,相當於JDK的ServiceLoader,也實現了Spring的IOC功能,通過ExtensionLoader配合URL完成對應類實現的載入,是Dubbo框架高度可拓展性實現的基礎。以下各個規則和實現都是在ExtensionLoader提供實現,如從META-INF目錄獲取SPI的類宣告,Adaptive註解配合URL引數或protocol,自適應獲取SPI介面實現類等。
自定義SPI
- 所有需要通過ExtensionLoader解析、設定屬性,自適應獲取實現類的介面,均需要增加@SPI宣告,如下為:Protocol介面:
- 增加了根據例項別名的概念,如:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol,預設java spi則只在META-INF/services目錄,包含介面全名作為檔案,檔案內容為各個實現類全名列表; - 增加存放spi定義檔案的目錄,包括:META-INF/dubbo,META-INF/dubbo/internal和META-INF/services,而不只是MATE-INF/services。
1,2 如圖:
Adaptive與Activate註解
-
@Adaptive為根據URL引數,在執行時動態決定選擇哪種介面實現,@Adaptive註冊可以用在實現類上,但是隻能放在介面的其中一個實現類上。更多的時候是將@Adaptive註解放在介面方法上,表示這些方法可以在執行時決定使用哪個spi實現類。如下為註冊工廠的介面:
在getRegistry方法上使用@Adaptive({“protocol”})表示:根據URL的protocol引數的值,如:zookeeper://xxx,決定使用以下哪個註冊工廠實現:
動態選擇介面實現的核心原始碼如下:
如上圖:為從method獲取Adaptive註解,然後如下圖:獲取@Adaptive註解的value陣列,如果沒有則根據介面名設定value。
如下圖: -
根據value的值,決定是從url的parameter獲取spi實現,還是根據URL的protocol選擇spi實現。即:如果value值為protocol則根據url的protocol,否則根據url中parameter選擇。
-
通過value產生了getNameCode的具體值,根據getNameCode得出了當前選擇的spi實現類的別名extName,最後通過getExtensionLoader(介面.class).getExtension(extName)載入最終呼叫的實現類。
-
Activate為根據URL引數,動態決定是否啟用某個類,如ProtocolFilter介面的實現類就是根據Activate規則,決定啟用哪些過濾器。
Wrapper包裝器
- 定義:某個介面的包裝類,實現該介面並且只包含一個以該介面作為唯一引數的建構函式的類
- 作用:用於增強、裝飾該介面實際提供功能的類的例項,即該介面的具體實現,多個包裝器類無序地包裝到該例項上。如Protocol介面包括兩個wrapper類分別為:
URL匯流排
dubbo為以URL為主線的一個框架,即根據URL中的相關引數,adaptive自適應選擇對應的spi實現類,核心實現為:
基本邏輯為:
根據建構函式傳入的type:
依次檢視type的每個方法的每個引數,如果存在引數型別為URL則就找到了;如果沒有,則繼續檢視引數所屬類的每個方法,是否存在“以get開頭,不存在引數,返回型別為URL”的方法,有則找到了。
案例:ServiceConfig服務註冊
在dubbo-bootstrap中,遍歷ServiceConfig列表,開始提供者的服務匯出註冊。
ServiceConfig的export匯出提供者的方法到註冊中心,從原始碼實現來看是通過ExtensionLoader以下聲明瞭一個protocol:
實際呼叫的是RegistryProtocol,而能判斷最終呼叫的是RegistryProtocol,是因為url為:registry://xxx,具體為invoker的url為registry://xxx,即url.getProtocol()返回為registry。
Protocol介面為方法export(Invoker invokder)對應的Invoker介面繼承於Node,Node介面包含URL getUrl()方法。
dubbo生成的adative類:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
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!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
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();
// 此處url.getProtocol不為null,返回registry
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)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 java.lang.Class {
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)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
參考:
https://blog.csdn.net/yangxiaobo118/article/details/80696830