Dubbo 源碼分析 —— 拓展機制 SPI

1. 概述

艿艿的友情提示:

這是一篇相對長的文章。

胖友可以帶着這樣的思維來理解 Dubbo SPI ,它提供了 Spring IOC、AOP 的功能。?

本文主要分享 Dubbo 的拓展機制 SPI

想要理解 Dubbo ,理解 Dubbo SPI 是非常必須的。在 Dubbo 中,提供了大量的拓展點,基於 Dubbo SPI 機制加載。如下圖所示:

Dubbo 拓展點

2. 改進

在看具體的 Dubbo SPI 實現之前,我們先理解 Dubbo SPI 產生的背景:

FROM 《Dubbo 開發指南 —— 拓展點加載》

Dubbo 的擴展點加載從 JDK 標準的 SPI (Service Provider Interface) 擴展點發現機制加強而來。

Dubbo 改進了 JDK 標準的 SPI 的以下問題:

  1. JDK 標準的 SPI 會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源。
  2. 如果擴展點加載失敗,連擴展點的名稱都拿不到了。比如:JDK 標準的 ScriptEngine,通過 getName() 獲取腳本類型的名稱,但如果 RubyScriptEngine 因爲所依賴的 jruby.jar 不存在,導致 RubyScriptEngine 類加載失敗,這個失敗原因被喫掉了,和 ruby 對應不起來,當用戶執行 ruby 腳本時,會報不支持 ruby,而不是真正失敗的原因。
  3. 增加了對擴展點 IoC 和 AOP 的支持,一個擴展點可以直接 setter 注入其它擴展點。
  • Dubbo 自己實現了一套 SPI 機制,而不是使用 Java 標準的 SPI 。
  • 第一點問題,Dubbo 有很多的拓展點,例如 Protocol、Filter 等等。並且每個拓展點有多種的實現,例如 Protocol 有 DubboProtocol、InjvmProtocol、RestProtocol 等等。那麼使用 JDK SPI 機制,會初始化無用的拓展點及其實現,造成不必要的耗時與資源浪費。
  • 第二點問題,【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/internal/ 例子
    • 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.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 屬性中。
    • 唯一參數爲拓展接口的構造方法的實現類,或者說拓展 Wrapper 實現類。例如,ProtocolFilterWrapper 。
      • 拓展 Wrapper 實現類,會添加到 cachedWrapperClasses 屬性中。
    • 總結來說,cachedClasses + cachedAdaptiveClass + cachedWrapperClasses 纔是完整緩存的拓展實現類的配置。
  • 第 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 數組。例如:ExtensionFactory 的配置文件
  • 第 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) 這個構造方法。
  • 第 69 至 105 行:若獲得構造方法失敗,則代表是普通的拓展實現類,緩存到 extensionClasses 變量中。
    • 第 70 行:調用 Class#getConstructor(Class<?>... parameterTypes) 方法,獲得參數爲空的構造方法。
    • 第 72 至 82 行:未配置拓展名,自動生成。適用於 Java SPI 的配置方式。例如,xxx.yyy.DemoFilter 生成的拓展名爲 demo
      • 第 73 行:通過 @Extension 註解的方式設置拓展名的方式已經廢棄,胖友可以無視該方法。
  • 第 84 行:獲得拓展名。使用逗號進行分割,即多個拓展名可以對應同一個拓展實現類。
  • 第 86 至 90 行:緩存 @Activate 到 cachedActivates 。在 「7. @Activate」 詳細解析。
  • 第 93 至 95 行:緩存到 cachedNames 屬性。
  • 第 96 至 102 行:緩存拓展實現類到 extensionClasses 變量。注意,相同拓展名,不能對應多個不同的拓展實現。
  • 第 108 至 112 行:若發生異常,記錄到異常集合 exceptions 屬性。

4.2.4 其他方法

如下方法,和該流程無關,胖友可自行查看。

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 行:處理自動激活的拓展對象們。
  • 第 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;
}

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 子類類圖如下: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 注入。

666. 彩蛋

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章