最近抽空開始了 Dubbo 源碼的閱讀之旅,但願能夠經過寫文章的方式記錄和分享本身對 Dubbo 的理解。若是在本文出現一些紕漏或者錯誤之處,也但願你們不吝指出。javascript
在閱讀本文以前可能須要你對 Java SPI(Service Provider Interface) 機制有過簡單的瞭解。這裏簡單介紹下:在面向對象的設計中,咱們提倡模塊之間基於接口編程。不一樣模塊可能會有不一樣的具體實現,可是爲了不模塊的之間的耦合過大,咱們須要一種有效的服務(服務實現)發現機制來選擇具體模塊。SPI 就是這樣一種基於接口編程+策略模式+配置文件,同時可供使用者根據本身的實際須要啓用/替換模塊具體實現的方案。html
如下內容摘錄自 https://dubbo.gitbooks.io/dubbo-dev-book/SPI.html
Dubbo 的擴展點加載從 JDK 標準的 SPI (Service Provider Interface) 擴展點發現機制增強而來。
JDK 標準的 SPI 會一次性實例化擴展點全部實現,若是有擴展實現初始化很耗時,但若是沒用上也加載,會很浪費資源。
若是擴展點加載失敗,連擴展點的名稱都拿不到了。好比:JDK 標準的 ScriptEngine,經過 getName() 獲取腳本類型的名稱,但若是 RubyScriptEngine 由於所依賴的 jruby.jar 不存在,致使 RubyScriptEngine 類加載失敗,這個失敗緣由被吃掉了,和 ruby 對應不起來,當用戶執行 ruby 腳本時,會報不支持 ruby,而不是真正失敗的緣由。
增長了對擴展點 IoC 和 AOP 的支持,一個擴展點能夠直接 setter 注入其它擴展點java
在 Dubbo 中,若是某個 interface 接口標記了 @SPI 註解,那麼咱們認爲它是 Dubbo 中的一個擴展點。擴展點是 Dubbo SPI 的核心,下面咱們就擴展點加載、擴展點自動包裝、擴展點自動裝配幾方面來聊聊具體實現。git
在閱讀本文前,若是你閱讀過Java SPI 相關內容,大概能回憶起來有 /META-INF/services 這樣一個目錄。在這個目錄下有一個以接口命名的文件,文件的內容爲接口具體實現類的全限定名。在 Dubbo 中咱們也能找到相似的設計。apache
很是好~咱們如今已經知道了從哪裏加載擴展點了,再回憶一下,JAVA SPI是如何加載的。編程
ServiceLoader<DubboService> spiLoader = ServiceLoader.load(XXX.class);
相似的,在 Dubbo 中也有這樣一個用於加載擴展點的類 ExtensionLoader。這一章節,咱們會着重瞭解一下這個類究竟是如何幫助咱們加載擴展點的。咱們先來看一段簡短的代碼。緩存
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
在 Dubbo 的實現裏面用到了大量相似的代碼片斷,咱們只須要提供一個 type ,便可獲取該 type 的自適應(關於自適應的理解在後文會提到)擴展類。在獲取對應自適應擴展類時,咱們首先獲取該類型的 ExtensionLoader。看到這裏咱們應該下意識的感受到對於每一個 type 來講,都應該有一個對應的 ExtensionLoader 對象。咱們先來看看 ExtensionLoader 是如何獲取的。ruby
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } // 是否被 SPI 註解標識 if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } //EXTENSION_LOADERS 爲一個 ConcurrentMap集合,key 爲 Class 對象,value 爲ExtenLoader 對象 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; } private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
上面這一段的代碼比較簡單,根據 type 從 EXTENSION_LOADERS 集合中獲取 loader ,若是返回的值爲 null 則新建一個 ExtensionLoader 對象。這裏的 objectFactory 獲取也用到了相似的方法,獲取到了 ExtensionFactory 的擴展自適應類。app
public T getAdaptiveExtension() { //cachedAdaptiveInstance用於緩存自適應擴展類實例 Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { // ... } } } } return (T) instance; }
getAdaptiveExtension()方法用於獲取當前自適應擴展類實例,首先會從 cachedAdaptiveInstance 對象中獲取,若是值爲 null 同時 createAdaptiveInstanceError 爲空,則調用 createAdaptiveExtension 方法建立擴展類實例。建立完後更新 cachedAdaptiveInstance 。ide
private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { // 省略異常 } }
這裏有兩個方法值得咱們關注,injectExtension() 和 getAdaptiveExtensionClass()。injectExtension() 看名字像是一個實現了注入功能的方法,而 getAdaptiveExtensionClass() 則用於獲取具體的自適應擴展類。咱們依次看下這兩個方法。
private T injectExtension(T instance) { try { if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { //若是存在 DisableInject 註解則跳過 if (method.getAnnotation(DisableInject.class) != null) { continue; } //獲取 method 第一個參數的類型 Class<?> pt = method.getParameterTypes()[0]; try { String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); if (object != null) { method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
簡單的總結下這個方法作了什麼:遍歷當前實例的 set 方法,以 set 方法第四位開始至末尾的字符串爲關鍵字,嘗試經過 objectFactory 來獲取對應的 擴展類實現。若是存在對應擴展類,經過反射注入到當前實例中。這個方法至關於完成了一個簡單的依賴注入功能,咱們常說 Dubbo 中的 IOC 實際上也是在這裏體現的。
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
接着看 getAdaptiveExtensionClass() 方法。首先調用 getExtensionClasses() 方法,若是 cachedAdaptiveClass() 不爲 null 則返回,若是爲 null 則調用 createAdaptiveExtensionClass() 方法。依次看下這兩個方法。
private Map<String, Class<?>> getExtensionClasses() { Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }
內容比較簡單,咱們直接看 loadExtensionClasses() 方法。
private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); // 只能有一個默認擴展實例 if (names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) { cachedDefaultName = names[0]; } } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }
繞來繞去這麼久,終於要進入主題了。爲何說進入主題了呢?看看這幾個變量的值
熟悉的配方熟悉的料。。沒錯了,咱們立刻就要開始讀取這三個目錄下的文件,而後開始加載咱們的擴展點了。
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { String fileName = dir + type; try { Enumeration<java.net.URL> urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { // ... } } private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); try { String line; while ((line = reader.readLine()) != null) { //#對應註釋位置,剔除存在的註釋 final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { try { String name = null; //文件中的內容以 key=value 的形式保存,拆分 key 和 vlaue int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { // ... } } } } finally { reader.close(); } } catch (Throwable t) { // ... } } private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // 用於判斷 class 是否是 type 接口的實現類 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface."); } // 若是當前 class 被 @Adaptive 註解標記,更新 cachedAdaptiveClass 緩存對象 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { // 省略異常 } } else if (isWrapperClass(clazz)) { // 這裏涉及到了 Dubbo 擴展點的另外一個機制:包裝,在後文介紹 Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } else { clazz.getConstructor(); // 若是 name 爲空,調用 findAnnotationName() 方法。若是當前類有 @Extension 註解,直接返回 @Extension 註解value; // 若沒有 @Extension 註解,可是類名相似 xxxType(Type 表明 type 的類名),返回值爲小寫的 xxx if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { // @Activate 註解用於配置擴展被自動激活條件 // 若是當前 class 包含 @Activate ,加入到緩存中 Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } else { // support com.alibaba.dubbo.common.extension.Activate com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class); if (oldActivate != null) { cachedActivates.put(names[0], oldActivate); } } for (String n : names) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class<?> c = extensionClasses.get(n); if (c == null) { // 還記得文件內容長啥樣嗎?(name = calssvalue),咱們最後將其保存到了 extensionClasses 集合中 extensionClasses.put(n, clazz); } else if (c != clazz) { // ... } } } } }
這一段代碼真的至關長啊。。梳理下以後發現其實他作的事情也很簡單:
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
Ok,咱們已經分析了 getExtensionClasses 方法,而且已經將擴展點實現加載到了緩存中。這個方法是由 getAdaptiveExtensionClass() 方法引出來的,它看起來是像是建立自適應擴展類的。這裏會先判斷緩存對象 cachedAdaptiveClass 是否會空,cachedAdaptiveClass 是何時被初始化的呢?回顧一下以前的代碼:
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // 省略... if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { // 省略... } } }
在 loadClass() 方法中若是發現當前 clazz 包含 @Adaptive 註解,則將當前 clazz 做爲緩存自適應類保存。例如在 AdaptiveExtensionFactory 類中就有這麼用,咱們會將 AdaptiveExtensionFactory 類做爲 ExtensionFactory 類型的自適應類緩存起來。
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory
咱們繼續分析該方法的後部分。若是 cachedAdaptiveClass 爲 null,則會調用 createAdaptiveExtensionClass() 方法動態生成一個自適應擴展類。
private Class<?> createAdaptiveExtensionClass() { String code = createAdaptiveExtensionClassCode(); System.out.println(code); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
這一段代碼在本次分享中不打算重點敘述,能夠簡單的理解爲 dubbo 幫我生成了一個自適應類。我摘取了生成的一段代碼,以下所示:
package org.apache.dubbo.rpc; import org.apache.dubbo.common.extension.ExtensionLoader; public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory { private static final org.apache.dubbo.common.logger.Logger logger = org.apache.dubbo.common.logger.LoggerFactory.getLogger(ExtensionLoader.class); private java.util.concurrent.atomic.AtomicInteger count = new java.util.concurrent.atomic.AtomicInteger(0); public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException { if (arg2 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg2; String extName = url.getParameter("proxy", "javassist"); if(extName == null) throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])"); org.apache.dubbo.rpc.ProxyFactory extension = null; try { extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName); }catch(Exception e){ if (count.incrementAndGet() == 1) { logger.warn("Failed to find extension named " + extName + " for type org.apache.dubbo.rpc.ProxyFactory, will use default extension javassist instead.", e); } extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension("javassist"); } return extension.getInvoker(arg0, arg1, arg2); } public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = url.getParameter("proxy", "javassist"); if(extName == null) throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])"); org.apache.dubbo.rpc.ProxyFactory extension = null; try { extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName); }catch(Exception e){ if (count.incrementAndGet() == 1) { logger.warn("Failed to find extension named " + extName + " for type org.apache.dubbo.rpc.ProxyFactory, will use default extension javassist instead.", e); } extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension("javassist"); } return extension.getProxy(arg0, arg1); } public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = url.getParameter("proxy", "javassist"); if(extName == null) throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])"); org.apache.dubbo.rpc.ProxyFactory extension = null; try { extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName); }catch(Exception e){ if (count.incrementAndGet() == 1) { logger.warn("Failed to find extension named " + extName + " for type org.apache.dubbo.rpc.ProxyFactory, will use default extension javassist instead.", e); } extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension("javassist"); } return extension.getProxy(arg0); } }
extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
這一段代碼實際上纔是自適應適配類的精髓,看看 extName 是怎麼來的?
String extName = url.getParameter("proxy", "javassist");
extName 又是從 url 中取得的,實際上 url 對於 Dubbo 來講是一種很是重要的上下文傳輸載體,在後續系列文章中你們會逐步感覺到。
public T getExtension(String name) { if (name == null || name.length() == 0) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { return getDefaultExtension(); } // 從緩存中讀取擴展實現類 Holder<Object> holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<Object>()); holder = cachedInstances.get(name); } Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name); holder.set(instance); } } } return (T) instance; }
上面的邏輯比較簡單,這裏也不贅述了,直接看 createExtension() 方法。
private T createExtension(String name) { Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); } }
getExtensionClasses() 方法在前文已經分析過了,可是須要注意的是:getExtensionClasses 返回給咱們的不過是使用 Class.forName() 加載過的類而已,充其量執行了裏面的靜態代碼段,而並不是獲得了真正的實例。真正的實例對象仍須要調用 class.newInstance() 方法才能獲取。
瞭解了這些以後咱們繼續看,咱們經過 getExtensionClasses() 嘗試獲取系統已經加載的 class 對象,經過 class 對象再去擴展實例緩存中取。若是擴展實例爲 null,調用 newInstance() 方法初始化實例,並放到 EXTENSION_INSTANCES 緩存中。以後再調用 injectExtension() 方法進行依賴注入。最後一段涉及到包裝類的用法,下一個章節進行介紹。
在 createExtension() 方法中有以下一段代碼:
private T createExtension(String name) { // ···省略··· Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; // ···省略··· }
還記得 wrapperClasses 在什麼地方被初始化的嗎?在前文中的 loadClass() 方法中咱們已經有介紹過。再回顧一下:
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // ···省略··· if (isWrapperClass(clazz)) { Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } // ···省略··· } private boolean isWrapperClass(Class<?> clazz) { try { clazz.getConstructor(type); return true; } catch (NoSuchMethodException e) { return false; } }
在看這個方法前咱們先了解下 Dubbo 中 wrapper 類的定義。
舉個例子:
class A { private A a; public A(A a){ this.a = a; } }
咱們能夠看到 A 類有一個以 A 爲參數的構造方法,咱們稱它爲複製構造方法。有這樣構造方法的類在 Dubbo 中咱們稱它爲 Wrapper 類。
繼續看 isWrapperClass() 方法,這個方法比較簡單,嘗試獲取 clazz 中以 type 爲參數的構造方法,若是能夠獲取到,則認爲 clazz 則是當前 type 類的包裝類。再結合上面的代碼,咱們會發如今加載擴展點時,咱們將對應 type 的包裝類緩存起來。
private T createExtension(String name) { // ···省略··· T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; // ···省略··· }
爲了更好的理解這段代碼,咱們假設當前 type 值爲 Protocol.class ,咱們能夠在 org.apache.dubbo.rpc.Protocol 文件中找到 Protocol 接口的包裝類 ProtocolFilterWrapper 和 ProtocolListenerWrapper,他們會依次被添加到 cachedWrapperClasses 集合中。依次遍歷 cachedWrapperClasses 集合,好比第一次取到的是 ProtocolFilterWrapper 類,則會以調用 ProtocolFilterWrapper 的複製構造方法將 instance 包裝起來。建立完 ProtocolFilterWrapper 對象實例後,調用 injectExtension() 進行依賴注入。此時 instance 已經爲 ProtocolFilterWrapper 的實例,繼續循環,會將 ProtocolFilterWrapper 類包裝在 ProtocolListenerWrapper 類中。所以咱們最後返回的是一個 ProtocolListenerWrapper 實例。最後調用時,仍會經過一層一層的調用,最後調用原始 instance 的方法。
這裏的包裝類有點相似 AOP 思想,咱們能夠經過一層一層的包裝,在調用擴展實現以前添加一些日誌打印、監控等自定義的操做。
上文中咱們已經討論過 Dubbo 中利用反射機制實現一個類 IOC 功能。在這一章節中,咱們再回顧一下 injectExtension() 方法,仔細的來看看 Dubbo 中 IOC 功能的實現。
createExtension() instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); private T injectExtension(T instance) { // ··· Class<?> pt = method.getParameterTypes()[0]; try { String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); if (object != null) { method.invoke(instance, object); } } // ··· } public class StubProxyFactoryWrapper implements ProxyFactory { // ... private Protocol protocol; public void setProtocol(Protocol protocol) { this.protocol = protocol; } //... }
在上一章節中咱們已經講過 wrapper 類,在這裏咱們舉個例子說明一下。好比咱們當前的 wrapperClass 類爲 StubProxyFactoryWrapper,那麼代碼執行邏輯大體以下所示:
@SPI("dubbo") public interface Protocol { } public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } }
在最後,咱們再回顧下開頭關於 Dubbo SPI 基於 JAVA SPI 改進的那段話:
Dubbo 的擴展點加載從 JDK 標準的 SPI (Service Provider Interface) 擴展點發現機制增強而來。
JDK 標準的 SPI 會一次性實例化擴展點全部實現,若是有擴展實現初始化很耗時,但若是沒用上也加載,會很浪費資源。
若是擴展點加載失敗,連擴展點的名稱都拿不到了。好比:JDK 標準的 ScriptEngine,經過 getName() 獲取腳本類型的名稱,但若是 RubyScriptEngine 由於所依賴的 jruby.jar 不存在,致使 RubyScriptEngine 類加載失敗,這個失敗緣由被吃掉了,和 ruby 對應不起來,當用戶執行 ruby 腳本時,會報不支持 ruby,而不是真正失敗的緣由。增長了對擴展點 IoC 和 AOP 的支持,一個擴展點能夠直接 setter 注入其它擴展點。
總結以下: