1. 程式人生 > >dubbo原始碼分析-ExtensionLoader發現機制和Adaptive註解應用

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,那些系統註解或者自定義註解的實現原理。