dubbo原始碼分析-ExtensionLoader發現機制和Adaptive註解應用
Dubbo的靈活性體現在每個系統功能點都可以動態擴充套件為新的實現,而且只需要額外配置,不需要修改dubbo原始碼,非常符合面向物件設計的開閉原則,其實現原理利用了JDK5.0的自動發現機制,具體使用相關程式碼是ExtensionLoader
本文目的:對Dubbo使用ExtensionLoader動態載入擴充套件點相關原始碼實現介紹
面向讀者:要求對dubbo的擴充套件點ExtensionLoader使用有基本的瞭解
背景介紹:官網連結-擴充套件點載入
- 擴充套件點配置
- 擴充套件點自適應
示例程式碼:來源於Dubbo原始碼,Git連結
相關術語:
擴充套件點 Extension - 可以理解為擴充套件某功能介面的實現類
關於擴充套件點配置
Dubbo的擴充套件點載入從JDK標準的SPI(Service Provider Interface)擴充套件點發現機制加強而來。具體在Dubbo應用實現如下。
服務載入配置檔案位於下面目錄:
程式碼能找到配置檔案,是由下面相關程式碼定義的:
public class ExtensionLoader<T> {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
private static final String SERVICES_DIRECTORY = "META-INF/services/" ;
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
如何根據副檔名載入對應實現類
具體例子:下面就是根據副檔名“mymock”載入對應類MockFilter:
MockFilter filter = (MockFilter)ExtensionLoader.getExtensionLoader(Filter.class ).getExtension("mymock");
“mymock”就是指定名字,定義在配置檔案內:
mymock=com.alibaba.dubbo.config.spring.filter.MockFilter
其中這裡的約定:MockFilter是介面Filter的實現類,檔名
com.alibaba.dubbo.rpc.Filter意思是介面類Filter在包com.alibaba.dubbo.rpc下
目錄結構:
Dubbo註解@Adaptive用法
-載入擴充套件點時,擴充套件點實現類的成員如果為其它擴充套件點型別,ExtensionLoader在會自動注入依賴的擴充套件點。
-ExtensionLoader通過掃描擴充套件點實現類的所有set方法來判定其成員。
即ExtensionLoader會執行擴充套件點的拼裝操作。
-ExtensionLoader注入的依賴擴充套件點是一個Adaptive例項,直到擴充套件點方法執行時才決定呼叫是一個擴充套件點實現。
1)Adaptive註解定義:
...
public @interface Adaptive {
...
String[] value() default {};
}
2)介面例子
@SPI("netty")
public interface Transporter {
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
...
...
}
這些註解在程式碼中如何發揮作用?
ExtensionLoader的createAdaptiveExtensionClassCode()方法根據註解@Adaptive的key值注入對應實現類。
● 首先知道ExtensionLoader注入的依賴擴充套件點是一個Adaptive例項,直到擴充套件點方法執行時才決定呼叫是哪一個擴充套件點實現。
● Dubbo使用URL物件(包含了Key-Value)傳遞配置資訊。key是在配置xml裡的定義使用,例如xml指定server = value,則key是‘server’, 然後Adaptive例項程式碼中指定載入的副檔名extName為value,根據此載入對應實現類。
幾點注意:
1)Adaptive例項的程式碼是由createAdaptiveExtensionClassCode()方法動態產生的。
2)”直到擴充套件點方法執行時才決定呼叫是哪一個擴充套件點實現”, 這句話理解為Adaptive程式碼中會根據extName副檔名指定載入對應實現類。
3)URL封裝的資訊裡可能含有從配置檔案xml傳遞過來的key=value資訊,假如沒有,則extName取介面類的註解@SPI(“netty”)中指定的名字,例如這裡是extName為Netty,這段邏輯程式碼如下圖部分片段:
java.lang.reflect.Method 物件反射獲取註解:
...
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
...
...
String[] value = adaptiveAnnotation.value();
...
上面程式碼片段相關邏輯在Adaptive註解中也有如何使用,具體以程式碼說明為準。
下圖附上protocol的程式碼生成Adaptive例項供參考:
extName的值就是根據上面邏輯生成的
(對於Protocol擴充套件的使用,URL傳給Protocol擴充套件點,基於擴充套件點的Adaptive機制,根據URL的協議頭,進行不同協議的服務暴露或引用)
拓展延伸
類似地,Wrapper包裝類是用Wrapper.makeWrapper方法動態生成新包裝類的。其它型別的註解使用也是在程式碼層面利用Class反射原理獲取註解及其值後生成程式碼。
啟發與收穫
Dubbo的設計有很多值得借鑑學習的地方,本文希望能夠讓你獲得啟發。
1. 開閉原則的面向物件設計,對於變化點的可擴充套件性,利用配置式服務發現機制動態載入相關介面實現類。
2. 程式碼動態生成,動態編譯技術。
3. 平時開源框架例如Spring,那些系統註解或者自定義註解的實現原理。