dubbo擴展的實現原理11

1       概要介紹及使用方法

1.1    Java SPI

關於 java spi 的介紹能夠參見下面這個帖子java

http://singleant.iteye.com/blog/1497259緩存

 

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

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

  • JDK標準的SPI會一次性實例化擴展點全部實現,若是有擴展實現初始化很耗時,但若是沒用上也加載,會很浪費資源
  • 若是擴展點加載失敗,連擴展點的名稱都拿不到了。好比:JDK標準的ScriptEngine,經過getName();獲取腳本類型的名稱, 但若是RubyScriptEngine由於所依賴的jruby.jar不存在,致使RubyScriptEngine類加載失敗,這個失敗緣由被吃掉 了,和ruby對應不起來,當用戶執行ruby腳本時,會報不支持ruby,而不是真正失敗的緣由
  • 增長了對擴展點IoCAOP的支持,一個擴展點能夠直接setter注入其它擴展點

 

1.2    擴展使用方法:

在擴展類的jar包內,放置擴展點配置文件:META-INF/dubbo/接口全限定名,內容爲:配置名=擴展實現類全限定名,多個實現類用換行符分隔。ide

示例:函數

在協議的實現jar包內放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol,內容爲:url

xxx=com.alibaba.xxx.XxxProtocolspa

實現類內容:代理

package com.alibaba.xxx;

import com.alibaba.dubbo.rpc.Protocol;

public class XxxProtocol implemenets Protocol {

    // ...

}

 

加載擴展實現, dubbo中都是經過ExtensionLoader實現的, 代碼如:code

ExtensionLoader.getExtensionLoader(Container.class).getExtension("registry");

2       ExtensionLoader代碼分析

咱們以方法getExtensionLoader做爲入口來分析加載的實現:

 public static<T> ExtensionLoader<T> getExtensionLoader(Class<T> type){

    if(type==null)

        throw new IllegalArgumentException("Extensiontype == null");

    if(!type.isInterface()){

        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");

    }

    if(!withExtensionAnnotation(type)){

        thrownewIllegalArgumentException("Extensiontype("+type+

   ") is not extension, because WITHOUT@" + SPI.class.getSimpleName() + " Annotation!");

    }



    ExtensionLoader<T> loader=(ExtensionLoader<T>)EXTENSION_LOADERS.get(type);

    if(loader==null){

        EXTENSION_LOADERS.putIfAbsent(type,newExtensionLoader<T>(type));

        loader=(ExtensionLoader<T>)EXTENSION_LOADERS.get(type);

    }

    return loader;

}

 

 

 

此方法根據擴展類型, 獲得一個擴展加載器(ExtensionLoader)

1, 經過方法開始部分的幾個校驗能夠知道:

·         擴展類泛型類型不能爲空

·         必須是一個接口.

·         此接口必須打上@SPI註解

2, 先從EXTENSION_LOADERS緩存裏取, 取不到會實例化一個ExtensionLoader, 而且緩存起來;

ExtensionLoader的構造器中, 會經過AdaptiveExtension方式獲得一個ExtensionFactory; AdaptiveExtensionExtensionFactory後面會再講到.

3,獲得ExtensionLoader以後, 再來看看getExtension(String name)方法

邏輯比較簡單,

·         若是name 傳一個 "true", 表示取默認擴展; getDefaultExtension(), 實現邏輯見4.

·         先從緩存cachedInstances裏取, 取不到經過createExtension方法建立, 完了再放緩存.

 

4, 獲取默認擴展的實現邏輯.getDefaultExtension

5, createExtension方法:

 1) 經過getExtensionClasses方法, 取得全部的擴展類緩存, 並從中取得name對應的class. getExtensionClasses實現邏輯見6.

·         根據類型, EXTENSION_INSTANCES得到緩存的實例.

·         若是不存在, 則經過class.newInstance建立實例, 並緩存.

·         經過injectExtension方法, 給這個實例注入各類屬性.injectExtension實現邏輯, 詳見7.

·         若是待擴展的T類型,Wrapper包裝器類(構造函數有T入參),實例化,而且injectExtension:

Set<Class<?>> wrapperClasses = cachedWrapperClasses;

    if(wrapperClasses != null && wrapperClasses.size() > 0){

        for(Class<?> wrapperClass : wrapperClasses){

        instance = injectExtension((T)wrapperClass.getConstructor(type).newInstance(instance));

    }

}

return instance;

 

能夠到看最後返回的是最後一個包裝器類;以前的包裝器類以及最開始最純粹的擴展類實例, 只是調用了一下構造器方法, 而後傳遞給後面的包裝器類; 這樣就能在各級包裝器類中添加各類擴展屬性級擴展方法(裝飾器模式).

 

包裝類在loadFile加載擴展類的時候, 加載到cachedWrapperClasses緩存的.(loadFile實現邏輯詳見8)

6.getExtensionClasses 獲取全部的擴展類, 實現邏輯

    1) cachedClasses緩存裏取, 若是不存在, 經過loadExtensionClasses方法加載.

    2) loadExtensionClasses實現邏輯:

        a.經過getExtensionLoader(Class<T>type)初始傳進來的type類型上的SPI註解的value, 解析獲得一個cachedDefaultName(這個就是默認擴展了.

        b.經過loadFile方法(實現邏輯見8), 加載DUBBO_INTERNAL_DIRECTORY,DUBBO_DIRECTORY,SERVICES_DIRECTORY三個目錄下的擴展類彙總到extensionClasses緩存, 最後一塊兒返回.

7.injectExtension方法,給實例注入屬性的實現邏輯.

經過反射, 找到只有一個入參的set public方法, 獲得參數類型.

經過ExtensionFactory獲得這個屬性的擴展實例, 若是存在的話, 就注入.

8, loadFile 經過配置文件加載擴展類的實現邏輯:

1)得到指定擴展配置文件名, :META-INF/dubbo/internal/com.alibaba.dubbo.common.compiler.Compiler

2)經過類加載器獲得配置文件資源Url, 在類路徑下可能會找到多個.

3)遍歷, 讀取配置文件中的每一行, 並解析, 配置行形如:javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

4)經過class.forName將等號後面的類, 轉爲類類型.

5)校驗:

a.必須是T接口的實現類 type.isAssignableFrom(clazz)

b. 若是這個類有@Adaptive註解. 而且緩存到cachedAdaptiveClass, 若是cachedAdaptiveClass已經有了, 但不是當前解析獲得的class, 說明該接口有多個@Adaptive註解擴展類, 報錯.關於@Adaptive後面會再分析.

c.若是不是@Adaptive擴展類, 嘗試得到該類帶有該類類型爲入參的構造函數

clazz.getConstructor(type);

存在, 則認爲是包裝器類, 加入到包裝器緩存.

d.若是不是包裝器類,

若是配置行, 僅有類名, 沒有=號及等號左邊的內容, 看看這個類上有沒有@Extension註解, 而且註解的值與類名

若是仍是沒有若是擴展類的類名是接口類類名結尾.

clazz.getSimpleName().length()>type.getSimpleName().length()

&&clazz.getSimpleName().endsWith(type.getSimpleName()

那麼 擴展名就是去掉接口名以後, 前半部分.

不然報錯.

這個擴展名, 容許逗號隔開, 配置多

從擴展類上獲取@Activate註解, 若是有, 存入cachedActivates緩存, 若是擴展名有多個, 只以第一個做爲緩存key.

遍歷每個擴展名, 放入緩存, 名稱擴展類對應擴展名.同時放入extensionClasses緩存,擴展名對應擴展類.

 

 

ExtensionLoader 還可加載Activate getActivateExtension()

 

Adaptive實例,直到擴展點方法執行時才決定調用是一個擴展點實現。

 

擴展點方法調用會有URL參數(或是參數有URL成員)

這樣依賴的擴展點也能夠從URL拿到配置信息,全部的擴展點本身定好配置的Key後,配置信息從URL上從最外層傳入。

 

Adaptive實例的邏輯是固定,指定提取的URLKey,便可以代理真正的實現類上,能夠動態生成。

 

DubboExtensionLoader的擴展點類開對應的Adaptive實現是在加載擴展點裏動態生成。指定提取的URLKey經過@Adaptive註解在接口方法上提供。

下面是DubboTransporter擴展點的代碼:

public interface Transporter {

    @Adaptive({"server", "transport"})

    Server bind(URL url, ChannelHandler handler) throws RemotingException;


    @Adaptive({"client", "transport"})

    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

 

對於bind方法表示,Adaptive實現先查找"server"key,若是該Key沒有值則找"transport"key值,來決定代理到哪一個實際擴展點。

 

調用擴展以下面的代碼:

Protocol refprotocol= ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

 

下面是getAdaptiveExtension()方法的實現邏輯:

 

1, cachedAdaptiveInstance緩存獲取Adaptive類實例, 若是不存在, 調用createAdaptiveExtension()方法建立, 並緩存

2, createAdaptiveExtension方法, 經過getAdaptiveExtensionClass()獲得Adaptive類並實例化, 而且經過injectExtension方法注入各屬性.

 

3, getAdaptiveExtensionClass()實現邏輯: 從緩存cachedAdaptiveClass中獲取, 取不到調用createAdaptiveExtensionClass方法建立

 

4, 經過createAdaptiveExtensionClassCode方法生成@Adaptive類字節碼, 而且經過Compiler類編譯獲得, Compiler也是經過ExtensionLoader getAdaptiveExtension獲得的.

這裏不會遞歸調用棧溢出嗎?

因爲在getExtensionClasses()中已經找了cachedAdaptiveClass(8.5.b), 因此若是cachedAdaptiveClass仍然爲空, 會去createAdaptiveExtensionClass() , 這樣就會調用堆棧溢出. 這樣就不難理解爲啥8.5.b, 若是找到多個@Adaptive註解的類會報異常.

 

Compiler有一個實現類加了@Adaptive註解, 因此不會再去createAdaptiveExtensionClass.

 

AdaptiveCompiler

 

@Adaptive

public class AdaptiveCompilerimplementsCompiler

相關文章
相關標籤/搜索