1. 概述
艿艿的友情提示:
這是一篇相對長的文章。
胖友可以帶着這樣的思維來理解 Dubbo SPI ,它提供了 Spring IOC、AOP 的功能。?
本文主要分享 Dubbo 的拓展機制 SPI。
想要理解 Dubbo ,理解 Dubbo SPI 是非常必須的。在 Dubbo 中,提供了大量的拓展點,基於 Dubbo SPI 機制加載。如下圖所示:
2. 改進
在看具體的 Dubbo SPI 實現之前,我們先理解 Dubbo SPI 產生的背景:
Dubbo 的擴展點加載從 JDK 標準的 SPI (Service Provider Interface) 擴展點發現機制加強而來。
Dubbo 改進了 JDK 標準的 SPI 的以下問題:
- JDK 標準的 SPI 會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源。
- 如果擴展點加載失敗,連擴展點的名稱都拿不到了。比如:JDK 標準的 ScriptEngine,通過 getName() 獲取腳本類型的名稱,但如果 RubyScriptEngine 因爲所依賴的 jruby.jar 不存在,導致 RubyScriptEngine 類加載失敗,這個失敗原因被喫掉了,和 ruby 對應不起來,當用戶執行 ruby 腳本時,會報不支持 ruby,而不是真正失敗的原因。
- 增加了對擴展點 IoC 和 AOP 的支持,一個擴展點可以直接 setter 注入其它擴展點。
- Dubbo 自己實現了一套 SPI 機制,而不是使用 Java 標準的 SPI 。
- 第一點問題,Dubbo 有很多的拓展點,例如 Protocol、Filter 等等。並且每個拓展點有多種的實現,例如 Protocol 有 DubboProtocol、InjvmProtocol、RestProtocol 等等。那麼使用 JDK SPI 機制,會初始化無用的拓展點及其實現,造成不必要的耗時與資源浪費。
- 如果無法理解的胖友,跟着 《Java SPI(Service Provider Interface)簡介》 文章,寫多個拓展實現,就很容易理解了。? 這就是概念呀。
- 第二點問題,【TODO 8009】ScriptEngine 沒看明白,不影響本文理解。
- 第三點問題,嚴格來說,這不算問題,而是增加了功能特性,在下文我們會看到。
3. 代碼結構
Dubbo SPI 在 dubbo-common
的 extension
包實現,如下圖所示:
4. ExtensionLoader
com.alibaba.dubbo.common.extension.ExtensionLoader
,拓展加載器。這是 Dubbo SPI 的核心。
4.1 屬性
1: private static final String SERVICES_DIRECTORY = "META-INF/services/"; 2: 3: private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; 4: 5: private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/"; 6: 7: private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); 8: 9: // ============================== 靜態屬性 ============================== 10: 11: /** 12: * 拓展加載器集合 13: * 14: * key:拓展接口 15: */ 16: private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); 17: /** 18: * 拓展實現類集合 19: * 20: * key:拓展實現類 21: * value:拓展對象。 22: * 23: * 例如,key 爲 Class<AccessLogFilter> 24: * value 爲 AccessLogFilter 對象 25: */ 26: private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>(); 27: 28: // ============================== 對象屬性 ============================== 29: 30: /** 31: * 拓展接口。 32: * 例如,Protocol 33: */ 34: private final Class<?> type; 35: /** 36: * 對象工廠 37: * 38: * 用於調用 {@link #injectExtension(Object)} 方法,向拓展對象注入依賴屬性。 39: * 40: * 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 屬性。 41: */ 42: private final ExtensionFactory objectFactory; 43: /** 44: * 緩存的拓展名與拓展類的映射。 45: * 46: * 和 {@link #cachedClasses} 的 KV 對調。 47: * 48: * 通過 {@link #loadExtensionClasses} 加載 49: */ 50: private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); 51: /** 52: * 緩存的拓展實現類集合。 53: * 54: * 不包含如下兩種類型: 55: * 1. 自適應拓展實現類。例如 AdaptiveExtensionFactory 56: * 2. 帶唯一參數爲拓展接口的構造方法的實現類,或者說拓展 Wrapper 實現類。例如,ProtocolFilterWrapper 。 57: * 拓展 Wrapper 實現類,會添加到 {@link #cachedWrapperClasses} 中 58: * 59: * 通過 {@link #loadExtensionClasses} 加載 60: */ 61: private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>(); 62: 63: /** 64: * 拓展名與 @Activate 的映射 65: * 66: * 例如,AccessLogFilter。 67: * 68: * 用於 {@link #getActivateExtension(URL, String)} 69: */ 70: private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); 71: /** 72: * 緩存的拓展對象集合 73: * 74: * key:拓展名 75: * value:拓展對象 76: * 77: * 例如,Protocol 拓展 78: * key:dubbo value:DubboProtocol 79: * key:injvm value:InjvmProtocol 80: * 81: * 通過 {@link #loadExtensionClasses} 加載 82: */ 83: private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); 84: /** 85: * 緩存的自適應( Adaptive )拓展對象 86: */ 87: private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); 88: /** 89: * 緩存的自適應拓展對象的類 90: * 91: * {@link #getAdaptiveExtensionClass()} 92: */ 93: private volatile Class<?> cachedAdaptiveClass = null; 94: /** 95: * 緩存的默認拓展名 96: * 97: * 通過 {@link SPI} 註解獲得 98: */ 99: private String cachedDefaultName; 100: /** 101: * 創建 {@link #cachedAdaptiveInstance} 時發生的異常。 102: * 103: * 發生異常後,不再創建,參見 {@link #createAdaptiveExtension()} 104: */ 105: private volatile Throwable createAdaptiveInstanceError; 106: 107: /** 108: * 拓展 Wrapper 實現類集合 109: * 110: * 帶唯一參數爲拓展接口的構造方法的實現類 111: * 112: * 通過 {@link #loadExtensionClasses} 加載 113: */ 114: private Set<Class<?>> cachedWrapperClasses; 115: 116: /** 117: * 拓展名 與 加載對應拓展類發生的異常 的 映射 118: * 119: * key:拓展名 120: * value:異常 121: * 122: * 在 {@link #loadFile(Map, String)} 時,記錄 123: */ 124: private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>(); |
- 第 1 至 5 行:在
META-INF/dubbo/internal/
和META-INF/dubbo/
目錄下,放置 接口全限定名 配置文件,每行內容爲:拓展名=拓展實現類全限定名。META-INF/dubbo/internal/
目錄下,從名字上可以看出,用於 Dubbo 內部提供的拓展實現。下圖是一個例子:META-INF/dubbo/
目錄下,用於用戶自定義的拓展實現。META-INF/service/
目錄下,Java SPI 的配置目錄。在 「4.2 加載拓展配置」 中,我們會看到 Dubbo SPI 對 Java SPI 做了兼容。
- 第 7 行:
NAME_SEPARATOR
,拓展名分隔符,使用逗號。 - 第 9 至 124 行,我們將屬性分成了兩類:1)靜態屬性;2)對象屬性。這是爲啥呢?
- 【靜態屬性】一方面,ExtensionLoader 是 ExtensionLoader 的管理容器。一個拓展( 拓展接口 )對應一個 ExtensionLoader 對象。例如,Protocol 和 Filter 分別對應一個 ExtensionLoader 對象。
- 【對象屬性】另一方面,一個拓展通過其 ExtensionLoader 對象,加載它的拓展實現們。我們會發現多個屬性都是 “cached“ 開頭。ExtensionLoader 考慮到性能和資源的優化,讀取拓展配置後,會首先進行緩存。等到 Dubbo 代碼真正用到對應的拓展實現時,進行拓展實現的對象的初始化。並且,初始化完成後,也會進行緩存。也就是說:
- 緩存加載的拓展配置
- 緩存創建的拓展實現對象
- ? 胖友先看下屬性的代碼註釋,有一個整體的印象。下面我們在讀實現代碼時,會進一步解析說明。
考慮到胖友能更好的理解下面的代碼實現,推薦先閱讀下 《Dubbo 開發指南 —— 擴展點加載》 文檔,建立下對 ExtensionLoader 特點的初步理解:
- 擴展點自動包裝
- 在 「4.4.2 createExtension」 詳細解析。
- 擴展點自動裝配
- 在 「4.4.3 injectExtension」 詳細解析。
- 擴展點自適應
- 在 「4.5 獲得自適應的拓展對象」 詳細解析。
- 擴展點自動激活
- 在 「4.6 獲得激活的拓展對象數組」 詳細解析。
4.2 獲得拓展配置
4.2.1 getExtensionClasses
#getExtensionClasses()
方法,獲得拓展實現類數組。
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>(); private volatile Class<?> cachedAdaptiveClass = null; private Set<Class<?>> cachedWrapperClasses; 1: /** 2: * 獲得拓展實現類數組 3: * 4: * @return 拓展實現類數組 5: */ 6: private Map<String, Class<?>> getExtensionClasses() { 7: // 從緩存中,獲得拓展實現類數組 8: Map<String, Class<?>> classes = cachedClasses.get(); 9: if (classes == null) { 10: synchronized (cachedClasses) { 11: classes = cachedClasses.get(); 12: if (classes == null) { 13: // 從配置文件中,加載拓展實現類數組 14: classes = loadExtensionClasses(); 15: // 設置到緩存中 16: cachedClasses.set(classes); 17: } 18: } 19: } 20: return classes; 21: } |
cachedClasses
屬性,緩存的拓展實現類集合。它不包含如下兩種類型的拓展實現:- 自適應拓展實現類。例如 AdaptiveExtensionFactory 。
- 拓展 Adaptive 實現類,會添加到
cachedAdaptiveClass
屬性中。
- 拓展 Adaptive 實現類,會添加到
- 帶唯一參數爲拓展接口的構造方法的實現類,或者說拓展 Wrapper 實現類。例如,ProtocolFilterWrapper 。
- 拓展 Wrapper 實現類,會添加到
cachedWrapperClasses
屬性中。
- 拓展 Wrapper 實現類,會添加到
- 總結來說,
cachedClasses
+cachedAdaptiveClass
+cachedWrapperClasses
纔是完整緩存的拓展實現類的配置。
- 自適應拓展實現類。例如 AdaptiveExtensionFactory 。
- 第 7 至 11 行:從緩存中,獲得拓展實現類數組。
- 第 12 至 14 行:當緩存不存在時,調用
#loadExtensionClasses()
方法,從配置文件中,加載拓展實現類數組。 - 第 16 行:設置加載的實現類數組,到緩存中。
4.2.2 loadExtensionClasses
#loadExtensionClasses()
方法,從多個配置文件中,加載拓展實現類數組。
1: /** 2: * 加載拓展實現類數組 3: * 4: * 無需聲明 synchronized ,因爲唯一調用該方法的 {@link #getExtensionClasses()} 已經聲明。 5: * // synchronized in getExtensionClasses 6: * 7: * @return 拓展實現類數組 8: */ 9: private Map<String, Class<?>> loadExtensionClasses() { 10: // 通過 @SPI 註解,獲得默認的拓展實現類名 11: final SPI defaultAnnotation = type.getAnnotation(SPI.class); 12: if (defaultAnnotation != null) { 13: String value = defaultAnnotation.value(); 14: if ((value = value.trim()).length() > 0) { 15: String[] names = NAME_SEPARATOR.split(value); 16: if (names.length > 1) { 17: throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() 18: + ": " + Arrays.toString(names)); 19: } 20: if (names.length == 1) cachedDefaultName = names[0]; 21: } 22: } 23: 24: // 從配置文件中,加載拓展實現類數組 25: Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); 26: loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); 27: loadFile(extensionClasses, DUBBO_DIRECTORY); 28: loadFile(extensionClasses, SERVICES_DIRECTORY); 29: return extensionClasses; 30: } |
- 第 10 至 22 行:通過
@SPI
註解,獲得拓展接口對應的默認的拓展實現類名。在 「5. @SPI」 詳細解析。 - 第 25 至 29 行:調用
#loadFile(extensionClasses, dir)
方法,從配置文件中,加載拓展實現類數組。注意,此處配置文件的加載順序。
4.2.3 loadFile
#loadFile(extensionClasses, dir)
方法,從一個配置文件中,加載拓展實現類數組。代碼如下:
/** * 緩存的自適應拓展對象的類 * * {@link #getAdaptiveExtensionClass()} */ private volatile Class<?> cachedAdaptiveClass = null; /** * 拓展 Wrapper 實現類集合 * * 帶唯一參數爲拓展接口的構造方法的實現類 * * 通過 {@link #loadExtensionClasses} 加載 */ private Set<Class<?>> cachedWrapperClasses; /** * 拓展名與 @Activate 的映射 * * 例如,AccessLogFilter。 * * 用於 {@link #getActivateExtension(URL, String)} */ private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); /** * 緩存的拓展名與拓展類的映射。 * * 和 {@link #cachedClasses} 的 KV 對調。 * * 通過 {@link #loadExtensionClasses} 加載 */ private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); /** * 拓展名 與 加載對應拓展類發生的異常 的 映射 * * key:拓展名 * value:異常 * * 在 {@link #loadFile(Map, String)} 時,記錄 */ private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>(); 1: /** 2: * 從一個配置文件中,加載拓展實現類數組。 3: * 4: * @param extensionClasses 拓展類名數組 5: * @param dir 文件名 6: */ 7: private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { 8: // 完整的文件名 9: String fileName = dir + type.getName(); 10: try { 11: Enumeration<java.net.URL> urls; 12: // 獲得文件名對應的所有文件數組 13: ClassLoader classLoader = findClassLoader(); 14: if (classLoader != null) { 15: urls = classLoader.getResources(fileName); 16: } else { 17: urls = ClassLoader.getSystemResources(fileName); 18: } 19: // 遍歷文件數組 20: if (urls != null) { 21: while (urls.hasMoreElements()) { 22: java.net.URL url = urls.nextElement(); 23: try { 24: BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); 25: try { 26: String line; 27: while ((line = reader.readLine()) != null) { 28: // 跳過當前被註釋掉的情況,例如 #spring=xxxxxxxxx 29: final int ci = line.indexOf('#'); 30: if (ci >= 0) line = line.substring(0, ci); 31: line = line.trim(); 32: if (line.length() > 0) { 33: try { 34: // 拆分,key=value 的配置格式 35: String name = null; 36: int i = line.indexOf('='); 37: if (i > 0) { 38: name = line.substring(0, i).trim(); 39: line = line.substring(i + 1).trim(); 40: } 41: if (line.length() > 0) { 42: // 判斷拓展實現,是否實現拓展接口 43: Class<?> clazz = Class.forName(line, true, classLoader); 44: if (!type.isAssignableFrom(clazz)) { 45: throw new IllegalStateException("Error when load extension class(interface: " + 46: type + ", class line: " + clazz.getName() + "), class " 47: + clazz.getName() + "is not subtype of interface."); 48: } 49: // 緩存自適應拓展對象的類到 `cachedAdaptiveClass` 50: if (clazz.isAnnotationPresent(Adaptive.class)) { 51: if (cachedAdaptiveClass == null) { 52: cachedAdaptiveClass = clazz; 53: } else if (!cachedAdaptiveClass.equals(clazz)) { 54: throw new IllegalStateException("More than 1 adaptive class found: " 55: + cachedAdaptiveClass.getClass().getName() 56: + ", " + clazz.getClass().getName()); 57: } 58: } else { 59: // 緩存拓展 Wrapper 實現類到 `cachedWrapperClasses` 60: try { 61: clazz.getConstructor(type); 62: Set<Class<?>> wrappers = cachedWrapperClasses; 63: if (wrappers == null) { 64: cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); 65: wrappers = cachedWrapperClasses; 66: } 67: wrappers.add(clazz); 68: // 緩存拓展實現類到 `extensionClasses` 69: } catch (NoSuchMethodException e) { 70: clazz.getConstructor(); 71: // 未配置拓展名,自動生成。例如,DemoFilter 爲 demo 。主要用於兼容 Java SPI 的配置。 72: if (name == null || name.length() == 0) { 73: name = findAnnotationName(clazz); 74: if (name == null || name.length() == 0) { 75: if (clazz.getSimpleName().length() > type.getSimpleName().length() 76: && clazz.getSimpleName().endsWith(type.getSimpleName())) { 77: name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); 78: } else { 79: throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); 80: } 81: } 82: } 83: // 獲得拓展名,可以是數組,有多個拓展名。 84: String[] names = NAME_SEPARATOR.split(name); 85: if (names != null && names.length > 0) { 86: // 緩存 @Activate 到 `cachedActivates` 。 87: Activate activate = clazz.getAnnotation(Activate.class); 88: if (activate != null) { 89: cachedActivates.put(names[0], activate); 90: } 91: for (String n : names) { 92: // 緩存到 `cachedNames` 93: if (!cachedNames.containsKey(clazz)) { 94: cachedNames.put(clazz, n); 95: } 96: // 緩存拓展實現類到 `extensionClasses` 97: Class<?> c = extensionClasses.get(n); 98: if (c == null) { 99: extensionClasses.put(n, clazz); 100: } else if (c != clazz) { 101: throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); 102: } 103: } 104: } 105: } 106: } 107: } 108: } catch (Throwable t) { 109: // 發生異常,記錄到異常集合 110: IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t); 111: exceptions.put(line, e); 112: } 113: } 114: } // end of while read lines 115: } finally { 116: reader.close(); 117: } 118: } catch (Throwable t) { 119: logger.error("Exception when load extension class(interface: " + 120: type + ", class file: " + url + ") in " + url, t); 121: } 122: } // end of while urls 123: } 124: } catch (Throwable t) { 125: logger.error("Exception when load extension class(interface: " + 126: type + ", description file: " + fileName + ").", t); 127: } 128: } |
- 第 9 行:獲得完整的文件名( 相對路徑 )。例如:
"META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory"
。 - 第 12 至 18 行:獲得文件名對應的所有文件 URL 數組。例如:
- 第 21 至 24 行:逐個文件 URL 遍歷。
- 第 27 行:逐行遍歷。
- 第 29 至 32 行:跳過當前被
"#"
註釋掉的情況,例如#spring=xxxxxxxxx
。 - 第 34 至 40 行:按照
key=value
的配置拆分。其中name
爲拓展名,line
爲拓展實現類名。注意,上文我們提到過 Dubbo SPI 會兼容 Java SPI 的配置格式,那麼按照此處的解析方式,name
會爲空。這種情況下,拓展名會自動生成,詳細見第 71 至 82 行的代碼。 - 第 42 至 48 行:判斷拓展實現類,需要實現拓展接口。
- 第 50 至 57 行:緩存自適應拓展對象的類到
cachedAdaptiveClass
屬性。在 「6. @Adaptive」 詳細解析。 - 第 59 至 67 行:緩存拓展 Wrapper 實現類到
cachedWrapperClasses
屬性。- 第 61 行:調用
Class#getConstructor(Class<?>... parameterTypes)
方法,通過反射的方式,參數爲拓展接口,判斷當前配置的拓展實現類爲拓展 Wrapper 實現類。若成功(未拋出異常),則代表符合條件。例如,ProtocolFilterWrapper(Protocol protocol) 這個構造方法。
- 第 61 行:調用
- 第 69 至 105 行:若獲得構造方法失敗,則代表是普通的拓展實現類,緩存到
extensionClasses
變量中。- 第 70 行:調用
Class#getConstructor(Class<?>... parameterTypes)
方法,獲得參數爲空的構造方法。 - 第 72 至 82 行:未配置拓展名,自動生成。適用於 Java SPI 的配置方式。例如,xxx.yyy.DemoFilter 生成的拓展名爲
demo
。- 第 73 行:通過
@Extension
註解的方式設置拓展名的方式已經廢棄,胖友可以無視該方法。
- 第 73 行:通過
- 第 70 行:調用
- 第 84 行:獲得拓展名。使用逗號進行分割,即多個拓展名可以對應同一個拓展實現類。
- 第 86 至 90 行:緩存
@Activate
到cachedActivates
。在 「7. @Activate」 詳細解析。 - 第 93 至 95 行:緩存到
cachedNames
屬性。 - 第 96 至 102 行:緩存拓展實現類到
extensionClasses
變量。注意,相同拓展名,不能對應多個不同的拓展實現。 - 第 108 至 112 行:若發生異常,記錄到異常集合
exceptions
屬性。
4.2.4 其他方法
如下方法,和該流程無關,胖友可自行查看。
#getExtensionClass(name)
#findException(name)
#getExtensionName(extensionInstance)
#getExtensionName(extensionClass)
#getSupportedExtensions()
#getDefaultExtensionName()
#hasExtension(name)
4.3 獲得拓展加載器
在 Dubbo 的代碼裏,常常能看到如下的代碼:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name) |
4.3.1 getExtensionLoader
#getExtensionLoader(type)
靜態方法,根據拓展點的接口,獲得拓展加載器。代碼如下:
/** * 拓展加載器集合 * * key:拓展接口 */ // 【靜態屬性】 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); 1: /** 2: * 根據拓展點的接口,獲得拓展加載器 3: * 4: * @param type 接口 5: * @param <T> 泛型 6: * @return 加載器 7: */ 8: @SuppressWarnings("unchecked") 9: public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { 10: if (type == null) 11: throw new IllegalArgumentException("Extension type == null"); 12: // 必須是接口 13: if (!type.isInterface()) { 14: throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); 15: } 16: // 必須包含 @SPI 註解 17: if (!withExtensionAnnotation(type)) { 18: throw new IllegalArgumentException("Extension type(" + type + 19: ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); 20: } 21: 22: // 獲得接口對應的拓展點加載器 23: ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 24: if (loader == null) { 25: EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 26: loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 27: } 28: } |
- 第 12 至 15 行:必須是接口。
- 第 16 至 20 行:調用
#withExtensionAnnotation()
方法,校驗必須使用@SPI
註解標記。 - 第 22 至 27 行:從
EXTENSION_LOADERS
靜態中獲取拓展接口對應的 ExtensionLoader 對象。若不存在,則創建 ExtensionLoader 對象,並添加到EXTENSION_LOADERS
。
4.3.2 構造方法
構造方法,代碼如下:
/** * 拓展接口。 * 例如,Protocol */ private final Class<?> type; /** * 對象工廠 * * 用於調用 {@link #injectExtension(Object)} 方法,向拓展對象注入依賴屬性。 * * 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 屬性。 */ private final ExtensionFactory objectFactory; 1: private ExtensionLoader(Class<?> type) { 2: this.type = type; 3: objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); 4: } |
objectFactory
屬性,對象工廠,功能上和 Spring IOC 一致。- 用於調用
#injectExtension(instance)
方法時,向創建的拓展注入其依賴的屬性。例如,CacheFilter.cacheFactory
屬性。 - 第 3 行:當拓展接口非 ExtensionFactory 時( 如果不加這個判斷,會是一個死循環 ),調用
ExtensionLoader#getAdaptiveExtension()
方法,獲得 ExtensionFactory 拓展接口的自適應拓展實現對象。爲什麼呢?在 「8. ExtensionFactory」 詳細解析。
- 用於調用
4.4 獲得指定拓展對象
在 Dubbo 的代碼裏,常常能看到如下的代碼:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name) |
4.4.1 getExtension
#getExtension()
方法,返回指定名字的擴展對象。如果指定名字的擴展不存在,則拋異常 IllegalStateException 。代碼如下:
/** * 緩存的拓展對象集合 * * key:拓展名 * value:拓展對象 * * 例如,Protocol 拓展 * key:dubbo value:DubboProtocol * key:injvm value:InjvmProtocol * * 通過 {@link #loadExtensionClasses} 加載 */ private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); 1: /** 2: * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException} 3: * will be thrown. 4: */ 5: /** 6: * 返回指定名字的擴展對象。如果指定名字的擴展不存在,則拋異常 {@link IllegalStateException}. 7: * 8: * @param name 拓展名 9: * @return 拓展對象 10: */ 11: @SuppressWarnings("unchecked") 12: public T getExtension(String name) { 13: if (name == null || name.length() == 0) 14: throw new IllegalArgumentException("Extension name == null"); 15: // 查找 默認的 拓展對象 16: if ("true".equals(name)) { 17: return getDefaultExtension(); 18: } 19: // 從 緩存中 獲得對應的拓展對象 20: Holder<Object> holder = cachedInstances.get(name); 21: if (holder == null) { 22: cachedInstances.putIfAbsent(name, new Holder<Object>()); 23: holder = cachedInstances.get(name); 24: } 25: Object instance = holder.get(); 26: if (instance == null) { 27: synchronized (holder) { 28: instance = holder.get(); 29: // 從 緩存中 未獲取到,進行創建緩存對象。 30: if (instance == null) { 31: instance = createExtension(name); 32: // 設置創建對象到緩存中 33: holder.set(instance); 34: } 35: } 36: } 37: return (T) instance; 38: } |
- 第 15 至 18 行:調用
#getDefaultExtension()
方法,查詢默認的拓展對象。在該方法的實現代碼中,簡化代碼爲getExtension(cachedDefaultName);
。 - 第 19 至 28 行:從緩存中,獲得拓展對象。
- 第 29 至 31 行:當緩存不存在時,調用
#createExtension(name)
方法,創建拓展對象。 - 第 33 行:添加創建的拓展對象,到緩存中。
4.4.2 createExtension
#createExtension(name)
方法,創建拓展名的拓展對象,並緩存。代碼如下:
/** * 拓展實現類集合 * * key:拓展實現類 * value:拓展對象。 * * 例如,key 爲 Class<AccessLogFilter> * value 爲 AccessLogFilter 對象 */ private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>(); 1: /** 2: * 創建拓展名的拓展對象,並緩存。 3: * 4: * @param name 拓展名 5: * @return 拓展對象 6: */ 7: @SuppressWarnings("unchecked") 8: private T createExtension(String name) { 9: // 獲得拓展名對應的拓展實現類 10: Class<?> clazz = getExtensionClasses().get(name); 11: if (clazz == null) { 12: throw findException(name); // 拋出異常 13: } 14: try { 15: // 從緩存中,獲得拓展對象。 16: T instance = (T) EXTENSION_INSTANCES.get(clazz); 17: if (instance == null) { 18: // 當緩存不存在時,創建拓展對象,並添加到緩存中。 19: EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); 20: instance = (T) EXTENSION_INSTANCES.get(clazz); 21: } 22: // 注入依賴的屬性 23: injectExtension(instance); 24: // 創建 Wrapper 拓展對象 25: Set<Class<?>> wrapperClasses = cachedWrapperClasses; 26: if (wrapperClasses != null && !wrapperClasses.isEmpty()) { 27: for (Class<?> wrapperClass : wrapperClasses) { 28: instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 29: } 30: } 31: return instance; 32: } catch (Throwable t) { 33: throw new IllegalStateException("Extension instance(name: " + name + ", class: " + 34: type + ") could not be instantiated: " + t.getMessage(), t); 35: } 36: } |
- 第 9 至 13 行:獲得拓展名對應的拓展實現類。若不存在,調用
#findException(name)
方法,拋出異常。 - 第 16 行:從緩存
EXTENSION_INSTANCES
靜態屬性中,獲得拓展對象。 - 第 17 至 21 行:當緩存不存在時,創建拓展對象,並添加到
EXTENSION_INSTANCES
中。因爲#getExtension(name)
方法中已經加synchronized
修飾,所以此處不用同步。 - 第 23 行:調用
#injectExtension(instance)
方法,向創建的拓展注入其依賴的屬性。 -
第 24 至 30 行:創建 Wrapper 拓展對象,將
instance
包裝在其中。在 《Dubbo 開發指南 —— 擴展點加載》 文章中,如此介紹 Wrapper 類:Wrapper 類同樣實現了擴展點接口,但是 Wrapper 不是擴展點的真正實現。它的用途主要是用於從 ExtensionLoader 返回擴展點時,包裝在真正的擴展點實現外。即從 ExtensionLoader 中返回的實際上是 Wrapper 類的實例,Wrapper 持有了實際的擴展點實現類。
擴展點的 Wrapper 類可以有多個,也可以根據需要新增。
通過 Wrapper 類可以把所有擴展點公共邏輯移至 Wrapper 中。新加的 Wrapper 在所有的擴展點上添加了邏輯,有些類似 AOP,即 Wrapper 代理了擴展點。
4.4.3 injectExtension
#injectExtension(instance)
方法,注入依賴的屬性。代碼如下:
1: /** 2: * 注入依賴的屬性 3: * 4: * @param instance 拓展對象 5: * @return 拓展對象 6: */ 7: private T injectExtension(T instance) { 8: try { 9: if (objectFactory != null) { 10: for (Method method : instance.getClass().getMethods()) { 11: if (method.getName().startsWith("set") 12: && method.getParameterTypes().length == 1 13: && Modifier.isPublic(method.getModifiers())) { // setting && public 方法 14: // 獲得屬性的類型 15: Class<?> pt = method.getParameterTypes()[0]; 16: try { 17: // 獲得屬性 18: String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; 19: // 獲得屬性值 20: Object object = objectFactory.getExtension(pt, property); 21: // 設置屬性值 22: if (object != null) { 23: method.invoke(instance, object); 24: } 25: } catch (Exception e) { 26: logger.error("fail to inject via method " + method.getName() 27: + " of interface " + type.getName() + ": " + e.getMessage(), e); 28: } 29: } 30: } 31: } 32: } catch (Exception e) { 33: logger.error(e.getMessage(), e); 34: } 35: return instance; 36: } |
- 第 9 行:必須有
objectFactory
屬性,即 ExtensionFactory 的拓展對象,不需要注入依賴的屬性。 - 第 10 至 13 行:反射獲得所有的方法,僅僅處理
public setting
方法。 - 第 15 行:獲得屬性的類型。
- 第 18 行:獲得屬性名。
- 第 20 行:獲得屬性值。注意,此處雖然調用的是
ExtensionFactory#getExtension(type, name)
方法,實際獲取的不僅僅是拓展對象,也可以是 Spring Bean 對象。答案在 「8. ExtensionFactory」 揭曉。 - 第 21 至 24 行:設置屬性值。
4.4.4 其他方法
如下方法,和該流程無關,胖友可自行查看。
4.5 獲得自適應的拓展對象
在 Dubbo 的代碼裏,常常能看到如下的代碼:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension() |
友情提示,胖友先看下 「6. Adaptive」 的內容,在回到此處。
4.5.1 getAdaptiveExtension
#getAdaptiveExtension()
方法,獲得自適應拓展對象。
/** * 緩存的自適應( Adaptive )拓展對象 */ private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); /** * 創建 {@link #cachedAdaptiveInstance} 時發生的異常。 * * 發生異常後,不再創建,參見 {@link #createAdaptiveExtension()} */ private volatile Throwable createAdaptiveInstanceError; 1: /** 2: * 獲得自適應拓展對象 3: * 4: * @return 拓展對象 5: */ 6: @SuppressWarnings("unchecked") 7: public T getAdaptiveExtension() { 8: // 從緩存中,獲得自適應拓展對象 9: Object instance = cachedAdaptiveInstance.get(); 10: if (instance == null) { 11: // 若之前未創建報錯, 12: if (createAdaptiveInstanceError == null) { 13: synchronized (cachedAdaptiveInstance) { 14: instance = cachedAdaptiveInstance.get(); 15: if (instance == null) { 16: try { 17: // 創建自適應拓展對象 18: instance = createAdaptiveExtension(); 19: // 設置到緩存 20: cachedAdaptiveInstance.set(instance); 21: } catch (Throwable t) { 22: // 記錄異常 23: createAdaptiveInstanceError = t; 24: throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); 25: } 26: } 27: } 28: // 若之前創建報錯,則拋出異常 IllegalStateException 29: } else { 30: throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); 31: } 32: } 33: return (T) instance; 34: } |
- 第 9 行:從緩存
cachedAdaptiveInstance
屬性中,獲得自適應拓展對象。 - 第 28 至 30 行:若之前創建報錯,則拋出異常 IllegalStateException 。
- 第 14 至 20 行:當緩存不存在時,調用
#createAdaptiveExtension()
方法,創建自適應拓展對象,並添加到cachedAdaptiveInstance
中。 - 第 22 至 24 行:若創建發生異常,記錄異常到
createAdaptiveInstanceError
,並拋出異常 IllegalStateException 。
4.5.2 createAdaptiveExtension
#createAdaptiveExtension()
方法,創建自適應拓展對象。代碼如下:
/** * 創建自適應拓展對象 * * @return 拓展對象 */ @SuppressWarnings("unchecked") private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } |
- 調用
#getAdaptiveExtensionClass()
方法,獲得自適應拓展類。 - 調用
Class#newInstance()
方法,創建自適應拓展對象。 - 調用
#injectExtension(instance)
方法,向創建的自適應拓展對象,注入依賴的屬性。
4.5.3 getAdaptiveExtensionClass
#getAdaptiveExtensionClass()
方法,獲得自適應拓展類。代碼如下:
1: /** 2: * @return 自適應拓展類 3: */ 4: private Class<?> getAdaptiveExtensionClass() { 5: getExtensionClasses(); 6: if (cachedAdaptiveClass != null) { 7: return cachedAdaptiveClass; 8: } 9: return cachedAdaptiveClass = createAdaptiveExtensionClass(); 10: } |
- 【
@Adaptive
的第一種】第 6 至 8 行:若cachedAdaptiveClass
已存在,直接返回。的第一種情況。 - 【
@Adaptive
的第二種】第 9 行:調用#createAdaptiveExtensionClass()
方法,自動生成自適應拓展的代碼實現,並編譯後返回該類。
4.5.4 createAdaptiveExtensionClassCode
#createAdaptiveExtensionClassCode()
方法,自動生成自適應拓展的代碼實現,並編譯後返回該類。
1: /** 2: * 自動生成自適應拓展的代碼實現,並編譯後返回該類。 3: * 4: * @return 類 5: */ 6: private Class<?> createAdaptiveExtensionClass() { 7: // 自動生成自適應拓展的代碼實現的字符串 8: String code = createAdaptiveExtensionClassCode(); 9: // 編譯代碼,並返回該類 10: ClassLoader classLoader = findClassLoader(); 11: com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); 12: return compiler.compile(code, classLoader); 13: } |
- 第 8 行:調用
#createAdaptiveExtensionClassCode
方法,自動生成自適應拓展的代碼實現的字符串。- ? 代碼比較簡單,已經添加詳細註釋,胖友點擊查看。
- 如下是 ProxyFactory 的自適應拓展的代碼實現的字符串生成例子
- 第 9 至 12 行:使用 Dubbo SPI 加載 Compier 拓展接口對應的拓展實現對象,後調用
Compiler#compile(code, classLoader)
方法,進行編譯。? 因爲不是本文的重點,後續另開文章分享。
4.6 獲得激活的拓展對象數組
在 Dubbo 的代碼裏,看到使用代碼如下:
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); |
4.6.1 getExtensionLoader
#getExtensionLoader(url, key, group)
方法,獲得符合自動激活條件的拓展對象數組。
1: /** 2: * This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)} 3: * 4: * 獲得符合自動激活條件的拓展對象數組 5: * 6: * @param url url 7: * @param key url parameter key which used to get extension point names 8: * Dubbo URL 參數名 9: * @param group group 10: * 過濾分組名 11: * @return extension list which are activated. 12: * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String) 13: */ 14: public List<T> getActivateExtension(URL url, String key, String group) { 15: // 從 Dubbo URL 獲得參數值 16: String value = url.getParameter(key); 17: // 獲得符合自動激活條件的拓展對象數組 18: return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group); 19: } 20: 21: /** 22: * Get activate extensions. 23: * 24: * 獲得符合自動激活條件的拓展對象數組 25: * 26: * @param url url 27: * @param values extension point names 28: * @param group group 29: * @return extension list which are activated 30: * @see com.alibaba.dubbo.common.extension.Activate 31: */ 32: public List<T> getActivateExtension(URL url, String[] values, String group) { 33: List<T> exts = new ArrayList<T>(); 34: List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values); 35: // 處理自動激活的拓展對象們 36: // 判斷不存在配置 `"-name"` 。例如,<dubbo:service filter="-default" /> ,代表移除所有默認過濾器。 37: if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { 38: // 獲得拓展實現類數組 39: getExtensionClasses(); 40: // 循環 41: for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) { 42: String name = entry.getKey(); 43: Activate activate = entry.getValue(); 44: if (isMatchGroup(group, activate.group())) { // 匹配分組 45: // 獲得拓展對象 46: T ext = getExtension(name); 47: if (!names.contains(name) // 不包含在自定義配置裏。如果包含,會在下面的代碼處理。 48: && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) // 判斷是否配置移除。例如 <dubbo:service filter="-monitor" />,則 MonitorFilter 會被移除 49: && isActive(activate, url)) { // 判斷是否激活 50: exts.add(ext); 51: } 52: } 53: } 54: // 排序 55: Collections.sort(exts, ActivateComparator.COMPARATOR); 56: } 57: // 處理自定義配置的拓展對象們。例如在 <dubbo:service filter="demo" /> ,代表需要加入 DemoFilter (這個是筆者自定義的)。 58: List<T> usrs = new ArrayList<T>(); 59: for (int i = 0; i < names.size(); i++) { 60: String name = names.get(i); 61: if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { // 判斷非移除的 62: // 將配置的自定義在自動激活的拓展對象們前面。例如,<dubbo:service filter="demo,default,demo2" /> ,則 DemoFilter 就會放在默認的過濾器前面。 63: if (Constants.DEFAULT_KEY.equals(name)) { 64: if (!usrs.isEmpty()) { 65: exts.addAll(0, usrs); 66: usrs.clear(); 67: } 68: } else { 69: // 獲得拓展對象 70: T ext = getExtension(name); 71: usrs.add(ext); 72: } 73: } 74: } 75: // 添加到結果集 76: if (!usrs.isEmpty()) { 77: exts.addAll(usrs); 78: } 79: return exts; 80: } |
- 第 16 行:從 Dubbo URL 獲得參數值。例如說,若 XML 配置 Service
<dubbo:service filter="demo, demo2" />
,並且在獲得 Filter 自動激活拓展時,此處就能解析到value=demo,demo2
。另外,value
可以根據逗號拆分。 - 第 18 行:調用
#getActivateExtension(url, values, group)
方法,獲得符合自動激活條件的拓展對象數組。 - 第 35 至 56 行:處理自動激活的拓展對象們。
#isMatchGroup(group, groups)
方法,匹配分組。#isActive(Activate, url)
方法,是否激活,通過 Dubbo URL 中是否存在參數名爲 `@Activate.value` ,並且參數值非空。
- 第 57 至 74 行:處理自定義配置的拓展對象們。
- 第 75 至 78 行:將
usrs
合併到exts
尾部。 - ? 代碼比較簡單,胖友直接看註釋。
4.6.2 ActivateComparator
com.alibaba.dubbo.common.extension.support.ActivateComparator
,自動激活拓展對象排序器。
- ? 代碼比較簡單,胖友直接看註釋。
5. @SPI
com.alibaba.dubbo.common.extension.@SPI
,擴展點接口的標識。代碼如下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface SPI { /** * default extension name */ String value() default ""; } |
-
value
,默認拓展實現類的名字。例如,Protocol 拓展接口,代碼如下:@SPI("dubbo") public interface Protocol { // ... 省略代碼 }
- 其中
"dubbo"
指的是 DubboProtocol ,Protocol 默認的拓展實現類。
- 其中
6. @Adaptive
com.alibaba.dubbo.common.extension.@Adaptive
,自適應拓展信息的標記。代碼如下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { /** * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed * in the URL, and the parameter names are given by this method. * <p> * If the specified parameters are not found from {@link URL}, then the default extension will be used for * dependency injection (specified in its interface's {@link SPI}). * <p> * For examples, given <code>String[] {"key1", "key2"}</code>: * <ol> * <li>find parameter 'key1' in URL, use its value as the extension's name</li> * <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li> * <li>use default extension if 'key2' doesn't appear either</li> * <li>otherwise, throw {@link IllegalStateException}</li> * </ol> * If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's * class name with the rule: divide classname from capital char into several parts, and separate the parts with * dot '.', for example: for {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}, its default name is * <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL. * * @return parameter key names in URL */ /** * 從 {@link URL }的 Key 名,對應的 Value 作爲要 Adapt 成的 Extension 名。 * <p> * 如果 {@link URL} 這些 Key 都沒有 Value ,使用 缺省的擴展(在接口的{@link SPI}中設定的值)。<br> * 比如,<code>String[] {"key1", "key2"}</code>,表示 * <ol> * <li>先在URL上找key1的Value作爲要Adapt成的Extension名; * <li>key1沒有Value,則使用key2的Value作爲要Adapt成的Extension名。 * <li>key2沒有Value,使用缺省的擴展。 * <li>如果沒有設定缺省擴展,則方法調用會拋出{@link IllegalStateException}。 * </ol> * <p> * 如果不設置則缺省使用Extension接口類名的點分隔小寫字串。<br> * 即對於Extension接口 {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper} 的缺省值爲 <code>String[] {"yyy.invoker.wrapper"}</code> * * @see SPI#value() */ String[] value() default {}; } |
@Adaptive
註解,可添加類或方法上,分別代表了兩種不同的使用方式。
友情提示:一個拓展接口,有且僅有一個 Adaptive 拓展實現類。
- 第一種,標記在類上,代表手動實現它是一個拓展接口的 Adaptive 拓展實現類。目前 Dubbo 項目裏,只有 ExtensionFactory 拓展的實現類 AdaptiveExtensionFactory 有這麼用。詳細解析見 「8.1 AdaptiveExtensionFactory」 。
- 第二種,標記在拓展接口的方法上,代表自動生成代碼實現該接口的 Adaptive 拓展實現類。
value
,從 Dubbo URL 獲取參數中,使用鍵名( Key ),獲取鍵值。該值爲真正的拓展名。- 自適應拓展實現類,會獲取拓展名對應的真正的拓展對象。通過該對象,執行真正的邏輯。
- 可以設置多個鍵名( Key ),順序獲取直到有值。若最終獲取不到,使用默認拓展名。
- 在 「4.5.4 createAdaptiveExtensionClassCode」 詳細解析。
7. @Activate
com.alibaba.dubbo.common.extension.@Activate
,自動激活條件的標記。代碼如下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Activate { /** * Activate the current extension when one of the groups matches. The group passed into * {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching. * * @return group names to match * @see ExtensionLoader#getActivateExtension(URL, String, String) */ /** * Group過濾條件。 * <br /> * 包含{@link ExtensionLoader#getActivateExtension}的group參數給的值,則返回擴展。 * <br /> * 如沒有Group設置,則不過濾。 */ String[] group() default {}; /** * Activate the current extension when the specified keys appear in the URL's parameters. * <p> * For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when * there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters. * </p> * * @return URL parameter keys * @see ExtensionLoader#getActivateExtension(URL, String) * @see ExtensionLoader#getActivateExtension(URL, String, String) */ /** * Key過濾條件。包含{@link ExtensionLoader#getActivateExtension}的URL的參數Key中有,則返回擴展。 * <p/> * 示例:<br/> * 註解的值 <code>@Activate("cache,validatioin")</code>, * 則{@link ExtensionLoader#getActivateExtension}的URL的參數有<code>cache</code>Key,或是<code>validatioin</code>則返回擴展。 * <br/> * 如沒有設置,則不過濾。 */ String[] value() default {}; /** * Relative ordering info, optional * * @return extension list which should be put before the current one */ /** * 排序信息,可以不提供。 */ String[] before() default {}; /** * Relative ordering info, optional * * @return extension list which should be put after the current one */ /** * 排序信息,可以不提供。 */ String[] after() default {}; /** * Absolute ordering info, optional * * @return absolute ordering info */ /** * 排序信息,可以不提供。 */ int order() default 0; } |
- 對於可以被框架中自動激活加載擴展,
@Activate
用於配置擴展被自動激活加載條件。比如,Filter 擴展,有多個實現,使用@Activate
的擴展可以根據條件被自動加載。- 這塊的例子,可以看下 《Dubbo 開發指南 —— 擴展點加載》「擴展點自動激活」 文檔提供的。
- ? 分成過濾條件和排序信息兩類屬性,胖友看下代碼裏的註釋。
- 在 「4.6 獲得激活的拓展對象數組」 詳細解析。
8. ExtensionFactory
com.alibaba.dubbo.common.extension.ExtensionFactory
,拓展工廠接口。代碼如下:
/** * ExtensionFactory * * 拓展工廠接口 */ @SPI public interface ExtensionFactory { /** * Get extension. * * 獲得拓展對象 * * @param type object type. 拓展接口 * @param name object name. 拓展名 * @return object instance. 拓展對象 */ <T> T getExtension(Class<T> type, String name); } |
- ExtensionFactory 自身也是拓展接口,基於 Dubbo SPI 加載具體拓展實現類。
#getExtension(type, name)
方法,在 「4.4.3 injectExtension」 中,獲得拓展對象,向創建的拓展對象注入依賴屬性。在實際代碼中,我們可以看到不僅僅獲得的是拓展對象,也可以是 Spring 中的 Bean 對象。- ExtensionFactory 子類類圖如下:
8.1 AdaptiveExtensionFactory
com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
,自適應 ExtensionFactory 拓展實現類。代碼如下:
1: @Adaptive 2: public class AdaptiveExtensionFactory implements ExtensionFactory { 3: 4: /** 5: * ExtensionFactory 拓展對象集合 6: */ 7: private final List<ExtensionFactory> factories; 8: 9: public AdaptiveExtensionFactory() { 10: // 使用 ExtensionLoader 加載拓展對象實現類。 11: ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); 12: List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); 13: for (String name : loader.getSupportedExtensions()) { 14: list.add(loader.getExtension(name)); 15: } 16: factories = Collections.unmodifiableList(list); 17: } 18: 19: public <T> T getExtension(Class<T> type, String name) { 20: // 遍歷工廠數組,直到獲得到屬性 21: for (ExtensionFactory factory : factories) { 22: T extension = factory.getExtension(type, name); 23: if (extension != null) { 24: return extension; 25: } 26: } 27: return null; 28: } 29: 30: } |
@Adaptive
註解,爲 ExtensionFactory 的自適應拓展實現類。- 構造方法,使用 ExtensionLoader 加載 ExtensionFactory 拓展對象的實現類。若胖友沒自己實現 ExtensionFactory 的情況下,
factories
爲 SpiExtensionFactory 和 SpringExtensionFactory 。 #getExtension(type, name)
方法,遍歷factories
,調用其#getExtension(type, name)
方法,直到獲得到屬性值。
8.2 SpiExtensionFactory
com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
,SPI ExtensionFactory 拓展實現類。代碼如下:
public class SpiExtensionFactory implements ExtensionFactory { /** * 獲得拓展對象 * * @param type object type. 拓展接口 * @param name object name. 拓展名 * @param <T> 泛型 * @return 拓展對象 */ public <T> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { // 校驗是 @SPI // 加載拓展接口對應的 ExtensionLoader 對象 ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); // 加載拓展對象 if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } } |
8.3 SpringExtensionFactory
com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
,Spring ExtensionFactory 拓展實現類。代碼如下:
public class SpringExtensionFactory implements ExtensionFactory { /** * Spring Context 集合 */ private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>(); public static void addApplicationContext(ApplicationContext context) { contexts.add(context); } public static void removeApplicationContext(ApplicationContext context) { contexts.remove(context); } @Override @SuppressWarnings("unchecked") public <T> T getExtension(Class<T> type, String name) { for (ApplicationContext context : contexts) { if (context.containsBean(name)) { // 獲得屬性 Object bean = context.getBean(name); // 判斷類型 if (type.isInstance(bean)) { return (T) bean; } } } return null; } } |
#getExtension(type, name)
方法,遍歷contexts
,調用其ApplicationContext#getBean(name)
方法,獲得 Bean 對象,直到成功並且值類型正確。
8.3.1 例子
DemoFilter 是筆者實現的 Filter 拓展實現類,代碼如下:
public class DemoFilter implements Filter { private DemoDAO demoDAO; @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } public DemoFilter setDemoDAO(DemoDAO demoDAO) { this.demoDAO = demoDAO; return this; } } |
-
DemoDAO ,筆者在 Spring 中聲明對應的 Bean 對象。
<bean id="demoDAO" class="com.alibaba.dubbo.demo.provider.DemoDAO" />
-
在 「4.4.3 injectExtension」 中,會調用
#setDemoDAO(demo)
方法,將 DemoFilter 依賴的屬性demoDAO
注入。