Dubbo——擴展點加載機制

擴展點加載機制


        只有標有@SPI註解的接口類纔會查找擴展點的實現,依次從下面這三個路徑讀取擴展點文件:META-INF/dubbo/internal 、META-INF/dubbo/ 、META-INF/services/,其中dubbo內部實現的各種擴展文件都放在META-INF/dubbo/internal目錄下面。

 

以com.alibaba.dubbo.rpc.ProxyFactory文件爲例,該文件中的內容如下

1 獲取ExtensionLoader實例

ExtensionLoader.getExtensionLoader(Class<T>type)方法:調用此靜態方法獲得ExtensionLoader對象實例,該方法對於每個Class對象創建一個ExtensionLoader對象實例。大致邏輯如下:

1)檢查type接口類是否使用了@SPI註解,只有使用了該註解的接口類纔會查找擴展點實現類;否則拋異常;

2)從全局靜態變量ExtensionLoader.EXTENSION_LOADERS:ConcurrentHashMap<Class<?>,ExtensionLoader<?>>中查找是否已經存在擴展點type的ExtensionLoader對象實例,若沒有則調用私有構造函數ExtensionLoader(Class<?>type);

3)在該私有構造函數中,完成ExtensionLoader對象的兩個成員變量的賦值,第一,成員變量type:Class賦值爲入參(擴展點type);第二,成員變量objectFactory:ExtensionFactory,若type爲ExtensionFactory.class,則objectFactory=null,否則通過調用ExtensionLoader. getExtensionLoader方法獲取ExtensionFactory.class的擴展點的ExtensionLoader實例,然後調用getAdaptiveExtension方法獲取擴展點實現類的適配器類實例。代碼如下:

2 getAdaptiveExtension()方法

該方法主要是獲取擴展點實現類的適配器類實例。首先從緩存變量ExtensionLoader.cachedAdaptiveInstance中獲取,若爲空,則調用createAdaptiveExtension方法創建適配器類的實例,並將適配器類的實例存入緩存變量ExtensionLoader.cachedAdaptiveInstance中。內部的調用邏輯如下:


 

2.1 createAdaptiveExtension方法

該方法完成適配器類的實例化並對適配器類的實例對象中類型爲擴展點接口類型的成員變量初始化值。

1)調用getAdaptiveExtensionClass方法,創建適配器類的class對象;

2)調用Class.newInstance方法完成class對象的實例化;

3)調用injectExtension方法完成實例化對象中成員變量的初始化工作。

 2.1.1 getAdaptiveExtensionClass方法

獲取擴展點實現類的Map對象,該Map對象的key爲每個類簡稱,value爲class對象。

1)檢查緩存ExtensionLoader.cachedClasses 變量中是否有值,若有則直接返回;

2)若沒有緩存,則調用loadExtensionClasses方法獲取Map對象,並存入ExtensionLoader.cachedClasses變量中。

2.1.1.1 getExtensionClasses方法

獲取擴展點實現類的Map對象,該Map對象的key爲每個類簡稱,value爲class對象。

1)檢查緩存ExtensionLoader.cachedClasses 變量中是否有值,若有則直接返回;

2)若沒有緩存,則調用loadExtensionClasses方法獲取Map對象,並存入ExtensionLoader.cachedClasses變量中。

 loadExtensionClasses方法主要是加載擴展點的實現類,業務邏輯如下:

1)獲取ExtensionLoader.type參數值(getExtensionLoader函數的入參)的類的SPI註解,獲取該註解的value值,若該值不爲空且只有一個值的話,則保存ExtensionLoader.cachedDefaultName對象,作爲默認實現類使用;若爲空則表示沒有指定默認的實現類;但SPI註解的value值一定不能設置多個。

2)調用loadFile方法解析以ExtensionLoader.type的類名來命名的文件內容,並返回Map對象,該對象的每個K-V即爲文件中的一行內容,key=類簡稱,value=class對象;

loadFile方法:逐行讀取以ExtensionLoader.type參數值的類名來命名的文件內容;ExtensionLoader.type的值是在ExtensionLoader.getExtensionLoader方法調用時傳入的。以ExtensionFactory爲例,文件內容的格式如下。

對每行的解析工作如下:

1)等號右邊爲實現類的類名。等號左邊爲該類的簡稱;若只寫類名,則以一定的規則來獲取類的簡稱,具體見下面的步驟實現;左邊的簡稱也可以是以逗號分隔的多個簡稱;

2)對等號右邊的類使用Class.forName方法創建class對象,後續對該class對象進行操作;

3)該class對象必須實現了ExtensionLoader.type參數指定的接口;

4)該class對象是否使用了@Adaptive註解,若使用了則將該class對象保存到ExtensionLoader.cachedAdaptiveClass 變量中,在每個文件中的所有實現類最多只能有一個類使用了@Adaptive註解;該行解析結束,繼續下一行;若沒有使用@Adaptive註解的類,則進行下面的解析步驟;

5)該class對象是否有ExtensionLoader.type參數的構造函數,若有則表示可以進行封裝,將該class存入ExtensionLoader.cachedWrapperClasses 變量中,該行解析結束,繼續下一行的解析;例如Protocol接口的擴展點文件中的ProtocolFilterWrapper類的記錄,該實現類有一個Protocol參數的構造函數,故是在創建Protocol的擴展點實現類的實例時用該類封裝一層,若沒有,則繼續下面的解析步驟;

6)若等號左邊爲空或者該行沒有等號,則檢查該class對象是否使用了@Extension註解,若使用了則取該註解的value值作爲該class對象的簡稱;若沒有使用@Extension註解,但實現類的類名是以接口名結尾的,則取類名的前部分爲簡稱;但若不是以接口名結尾的類名,則以類名爲該類的簡稱;

7)檢查class對象是否使用了@Activate註解;若使用則獲取該註解,並存入ExtensionLoader.cachedActivates對象中,key爲類的簡稱,若有多個簡稱則取第一個;

8)遍歷所有的類簡稱,將class對象和每個類簡稱存入ExtensionLoader.cachedNames對象中,其中key=class對象,value=類簡稱;將每個類簡稱和class對象存入Map對象並返回,key=類簡稱,value=class對象;

 

2.1.1.2 createAdaptiveExtensionClass方法

該方法在運行時動態創建適配器類。

1)動態生成適配器類的代碼,下面列出了部分接口的動態適配器類的代碼:CacheFactory、Cluster、ConfiguratorFactory、Dispatcher、HttpBinder、MonitorFactory、Protocol、ProxyFactory、RegistryFactory、RouterFactory、ThreadPool、Transporter、Validation、ZookeeperTransporter,具體代碼見《附件一》。在生成適配器類時,若接口中沒有聲明帶@Adaptive註解的方法,則不生成適配器類,若有,則在生成適配器類時,對帶註解的方法重寫;

2)編譯適配器類代碼,創建Class對象。編譯器也是通過ExtensionLoader類來動態擴展的,默認選擇JavassistCompiler編譯器。

2.1.1  injectExtension方法

對於非ExtensionFactory.Class對象,利用ExtensionFactory的擴展實現類的對象獲取成員變量的初始化值,實現代碼如下:

1)獲取公有的set方法,解析出成員變量的名稱。

2)調用ExtensionFactory.getExtension方法獲取成員變量的值。

3)通過反射的方式調用set方法完成成員變量值的注入。


上述代碼中的ExtensionFactory.getExtension方法:在ExtensionFactory的文件中,包含了AdaptiveExtensionFactory類,該類使用了@Adaptive註解,所以ExtensionFactory接口的適配器爲AdaptiveExtensionFactory類,故調用的是該適配器類的getExtension方法。

1)在該方法中遍歷ExtensionFactory擴展文件中的所有ExtensionFactory對象的getExtension方法,代碼如下:


2)目前有SpiExtensionFactory和SpringExtensionFactory兩種;第一,對於SpiExtensionFactory.getExtension方法,根據成員變量的類型從擴展點中加載該對象,代碼如下:


第二,對於SpringExtensionFactory.getExtension 方法,從Spring的上下文中獲取該成員變量的Bean對象,代碼如下:


2.1 getExtension(String name)方法

ExtensionLoader.getExtension(Stringname)方法:返回指定名字的擴展類實例,調用順序圖如下:

大致邏輯如下:

1)如果指定名字的擴展類不存在,則拋異常。

2)如果指定的名字爲“true”,則返回默認的實現類實例,即name= cachedDefaultName;

3)從ExtensionLoader.cachedInstances:ConcurrentHashMap變量中獲取該name的實例;

4)若Map中沒有該name的實例,則調用createExtension方法創建該實例,並保存到緩存中。

 1、createExtension(String name)方法:創建擴展點實現類的實例對象。

1)調用getExtensionClasses方法,獲取type參數的文件內容;並從Map對象中獲取name的Class對象,若沒有則拋異常;

2)從全局變量ExtensionLoader.EXTENSION_INSTANCES:ConcurrentMap<Class<?>, Object>中獲取該class的實例,若沒有,則創建該Class對象的實例,並緩存到上面的Map變量中;

3)調用injectExtension方法完成成員變量的初始化值注入工作;

4)遍歷cachedWrapperClasses變量,利用以type接口爲參數的構造函數創建每個封裝類,故對擴展類的實例進行了封裝。並調用injectExtension方法對封裝類的成員變量進行初始化工作;

5)返回的實例是經過封裝之後的實例對象。

 

2.2  getActivateExtension(URL url, String key, String group)方法

ExtensionLoader.getActivateExtension(URLurl, String key, String group)方法:主要是獲取當前擴展的所有可自動激活的實現類的實例列表exts:List<T>。調用順序圖如下:


大致邏輯如下:

1)調用getExtensionClasses方法,加載當前擴展接口的所有實現,會獲取到當前Extension中所有標有@Active註解的實現類,存入Map變量cachedActivates中,其中key值爲類名;

2)遍歷cachedActivates集合,檢查集合中每個類中@Active註解的group值是否與入參的值一致,若一致再檢查@Active註解的value值是否在URL參數中,若存在則以cachedActivates變量的key值爲參數調用ExtensionLoader.getExtension(Stringname)方法獲取@Active註解類的實例並存入exts列表中;

3)使用ActivateComparator對exts列表的集合進行排序,排序的依據主要是@Active註解的before、after、order的值來定;

4)以入參key爲參數調用ExtensionLoader.getExtension(Stringname)方法,獲取key值指定名字的擴展類實例,並將該實例存入exts列表中。例如在服務暴露過程中對Invoker代理對象進行過濾器鏈的封裝時,key=“service.filter”,則從URL中獲取該參數的值,該值是在<dubbo:service>標籤的filter屬性中配置的服務調用過程攔截器名稱,多個名稱用逗號分隔;在服務引用過程中對Invoker代理對象進行監聽器鏈的封裝時,key=“invoker.listener ”;

5)最後將所有的exts列表返回。




發佈了59 篇原創文章 · 獲贊 56 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章