dubbo之可擴(kuò)展機(jī)制SPI源碼分析

dubbo特性


image.png

其中的高度可擴(kuò)展能力就是針對(duì)Protocol愿吹、Transport、Serialization等被設(shè)計(jì)為擴(kuò)展點(diǎn)庇配。
擴(kuò)展機(jī)制SPI測(cè)試效果:


image.png

SPI的可擴(kuò)展機(jī)制的原理:
以Protocol為例宾濒,首先檢查Protocol類是否添加了@SPI注解,然后查找Protocol依賴包中是否含有key值是http的實(shí)現(xiàn)類鸠补。
@SPI注解

org.apache.dubbo.rpc.Protocol.class
Protocol協(xié)議類添加了@SPI注解,@SPI("dubbo")中的dubbo表示Protocol的默認(rèn)實(shí)現(xiàn)嘀掸。其中export和refer添加了@Adaptive注解紫岩,是在服務(wù)導(dǎo)出和服務(wù)引用使用的

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

}
http對(duì)應(yīng)的類HttpProtocol

在測(cè)試SPI項(xiàng)目中,由于只在依賴中添加了dubbo-rpc-http


image.png

所以只能獲取到\resources\META-INF\dubbo\internal目錄下的org.apache.dubbo.rpc.Protocol文件的http和http1對(duì)應(yīng)的值睬塌。


image.png

其中Adaptive注解表示自適應(yīng)泉蝌,Activate表示激活的

getExtensionLoader

獲取指定類型的擴(kuò)展類加載類對(duì)象
1、獲取ExtensionLoader對(duì)象
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionLoader


image.png

判斷type是空衫仑、不是個(gè)接口梨与、沒有實(shí)現(xiàn)@SPI注解直接返回
2、創(chuàng)建ExtensionLoader
根據(jù)type類型創(chuàng)建ExtensionLoader類
org.apache.dubbo.common.extension.ExtensionLoader#ExtensionLoader


image.png

ExtensionFactory是擴(kuò)展實(shí)例工廠文狱,也是SPI實(shí)現(xiàn)粥鞋。如果type不等于ExtensionFactory則objectFactory就是獲取ExtensionFactory接口的Adaptive實(shí)現(xiàn)類。
ExtensionFactory的實(shí)現(xiàn)類:
①瞄崇、SpiExtensionFactory SPI相關(guān)
②呻粹、SpringExtensionFactory 與spring相關(guān)的擴(kuò)展類工廠

③壕曼、AdaptiveExtensionFactory 決定是用SpiExtensionFactory或SpringExtensionFactory來獲取實(shí)例


image.png

3、getAdaptiveExtension
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension
image.png

得到Adaptive擴(kuò)展示例并添加到cachedAdaptiveInstance緩存中
4等浊、createAdaptiveExtension
org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtension
image.png

5腮郊、getAdaptiveExtensionClass
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtensionClass
獲取當(dāng)前接口的所有擴(kuò)展類、如果某個(gè)接口沒有手動(dòng)指定一個(gè)Adaptive類筹燕,那么就自動(dòng)生成一個(gè)Adaptive類
6轧飞、getExtensionClasses
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses
加載
image.png

7、loadExtensionClasses
org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses
加載擴(kuò)展類的文件夾dir
①撒踪、DUBBO_INTERNAL_DIRECTORY = META-INF/dubbo/internal/
②过咬、DUBBO_DIRECTORY = META-INF/dubbo/
③、SERVICES_DIRECTORY = META-INF/services/


image.png

ExtensionFactory的擴(kuò)展類
image.png

8制妄、loadDirectory
org.apache.dubbo.common.extension.ExtensionLoader#loadDirectory
加載文件夾dir和type.getName()組成的文件中加載數(shù)據(jù)掸绞。
例如上面的測(cè)試代碼就是從\resources\META-INF\dubbo\internal目錄下的org.apache.dubbo.rpc.Protocol文件加載的
image.png

9、loadResource
org.apache.dubbo.common.extension.ExtensionLoader#loadResource
加載數(shù)據(jù)流
image.png

image.png

10耕捞、loadClass
org.apache.dubbo.common.extension.ExtensionLoader#loadClass
image.png

image.png

①衔掸、緩存Adaptive
把添加Adaptive注解的當(dāng)前接口實(shí)現(xiàn)類添加到cachedAdaptiveClass中
image.png

②、Wrapper類
org.apache.dubbo.common.extension.ExtensionLoader#isWrapperClass
判斷一個(gè)類是不是Wrapper類俺抽,根據(jù)構(gòu)造方法判斷敞映。構(gòu)造方法參數(shù)類型是當(dāng)前接口,返回true
image.png

③凌埂、緩存Activate類
org.apache.dubbo.common.extension.ExtensionLoader#cacheActivateClass
image.png

11驱显、createAdaptiveExtensionClass
所以在第五步⑤getAdaptiveExtensionClass方法得到Adaptive自適應(yīng)的擴(kuò)展類時(shí)诗芜,如果緩存中已經(jīng)存在則直接返回瞳抓,如果沒有則會(huì)創(chuàng)建一個(gè)
image.png

org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass
image.png

12、得到getAdaptiveExtension
(與第三步對(duì)應(yīng))org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension


image.png

13伏恐、ExtensionLoader
(與第一步對(duì)應(yīng))得到type=interface org.apache.dubbo.rpc.Protocol
new ExtensionLoader<T>(type)得到的ExtensionLoader
image.png

getExtension

獲取指定擴(kuò)展類extensionLoader.getExtension("http")


image.png

org.apache.dubbo.common.extension.ExtensionLoader#createExtension
1孩哑、獲取當(dāng)前接口的所有擴(kuò)展類


image.png

①、getExtensionClasses加載擴(kuò)展類
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses
image.png

②翠桦、loadExtensionClasses從資源文件加載擴(kuò)展類
org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses


image.png
Dubbo的IOC

2横蜒、依賴注入。屬性注入销凑,通過setXxx方法
依賴注入


image.png

org.apache.dubbo.common.extension.ExtensionLoader#injectExtension


image.png

①丛晌、遍歷當(dāng)前實(shí)例的所有方法,這里注入是通過set方法注入的斗幼,所以在找set方法
image.png

②澎蛛、setter方法的參數(shù)類型pt

③、截取setter方法所對(duì)應(yīng)的屬性名property
property值是setXxx中的Xxx
④蜕窿、objectFactory工廠得到一個(gè)對(duì)象谋逻,會(huì)從Spring容器或通過SPI機(jī)制得到一個(gè)對(duì)象(Adaptive對(duì)象)
⑤呆馁、反射調(diào)用setter方法進(jìn)行注入
method.invoke把需要注入對(duì)象通過執(zhí)行setXxx方法設(shè)置到當(dāng)前instance對(duì)象的屬性值中(與spring中的Autowire效果相同)

Dubbo中的AOP

3、Wrapper實(shí)例進(jìn)行依賴注入
Wrapper實(shí)現(xiàn)了AOP的效果
從cachedWrapperClasses緩存中獲取Wrapper對(duì)當(dāng)前實(shí)例進(jìn)行包裹傳入的參數(shù)是instance對(duì)象毁兆,可一層層包裹

Adaptive

AdaptiveClass表示自適應(yīng)類浙滤,自適應(yīng)類可以通過getAdaptiveExtension獲取。只要在類上添加一個(gè)@Adaptive注解气堕,就表示當(dāng)前類是這個(gè)實(shí)現(xiàn)類接口的自適應(yīng)類纺腊,如果沒有添加了Adaptive注解的類,在我們獲取這個(gè)類的自適應(yīng)類時(shí)茎芭,就會(huì)通過createAdaptiveExtensionClass代碼自動(dòng)生成的一個(gè)自適應(yīng)類摹菠。

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    
    public void destroy()  {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort()  {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    
    public org.apache.dubbo.rpc.Exporter export(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.getProtocol() == null ? "dubbo" : url.getProtocol() );

        if(extName == null) 
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        
        return extension.export(arg0);
    }

    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {

        if (arg1 == null) throw new IllegalArgumentException("url == null");

        org.apache.dubbo.common.URL url = arg1;

        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");

        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);

        return extension.refer(arg0, arg1);
    }
}

這里獲取的Protocol/$Adaptive自適應(yīng)類中,只有export導(dǎo)出服務(wù)和refer引入服務(wù)進(jìn)行了Adaptive代理骗爆。是因?yàn)檫@兩個(gè)方法添加了Adaptive注解次氨。通過refer和export的比較可以看出,并不是加了Adaptive注解的就一定可以Adaptive代理摘投,arg1表示資源URL煮寡,arg1不能為空也就是說必須包含資源URL對(duì)象(無論是直接傳入還是其類中存在getUrl方法可以獲取到url),其關(guān)鍵就是URL包含的key(extName)的傳入犀呼。

Activate

Activate注解包含group和value的區(qū)分幸撕,就是為了匹配得到適用于不同場景的擴(kuò)展類。
group組分為PROVIDER和CONSUMER外臂,表示該擴(kuò)展點(diǎn)能在服務(wù)提供者端坐儿,或消費(fèi)端使用。
value表示參數(shù)的鍵宋光,當(dāng)調(diào)用getActivateExtension方法來獲取擴(kuò)展類時(shí)貌矿,如果傳入的url中的參數(shù)的key中,包括value的值罪佳,那么則表示當(dāng)前url可以使用這個(gè)擴(kuò)展點(diǎn)逛漫。


image.png

總結(jié):

dubbo的高度可擴(kuò)展能力體現(xiàn)在SPI上,SPI的源碼實(shí)現(xiàn)機(jī)制就是通過ExtensionLoader獲取指定的實(shí)現(xiàn)擴(kuò)展類赘艳。
ExtensionLoader擴(kuò)展加載類具體的實(shí)現(xiàn)就是加載對(duì)應(yīng)的資源文件( 通過目錄META-INF/dubbo/internal/酌毡、META-INF/dubbo/、META-INF/dubbo/與接口名字拼接的文件)得到擴(kuò)展類集合蕾管。
Adaptive類的生成和作用枷踏,Adaptive表示一個(gè)Adaptive代理類,可以通過資源URL調(diào)用指定擴(kuò)展類
Activate表示擴(kuò)展類的使用范圍用group組和url參數(shù)的鍵是否含有value來判斷
如果需要自定義擴(kuò)展只要遵循這個(gè)規(guī)則就可以了掰曾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載旭蠕,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市下梢,隨后出現(xiàn)的幾起案子客蹋,更是在濱河造成了極大的恐慌,老刑警劉巖孽江,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讶坯,死亡現(xiàn)場離奇詭異,居然都是意外死亡岗屏,警方通過查閱死者的電腦和手機(jī)辆琅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來这刷,“玉大人婉烟,你說我怎么就攤上這事∠疚荩” “怎么了似袁?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咐刨。 經(jīng)常有香客問我昙衅,道長,這世上最難降的妖魔是什么定鸟? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任而涉,我火速辦了婚禮,結(jié)果婚禮上联予,老公的妹妹穿的比我還像新娘啼县。我一直安慰自己,他們只是感情好沸久,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布季眷。 她就那樣靜靜地躺著,像睡著了一般麦向。 火紅的嫁衣襯著肌膚如雪瘟裸。 梳的紋絲不亂的頭發(fā)上客叉,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天诵竭,我揣著相機(jī)與錄音,去河邊找鬼兼搏。 笑死卵慰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的佛呻。 我是一名探鬼主播裳朋,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼吓著!你這毒婦竟也來了鲤嫡?” 一聲冷哼從身側(cè)響起送挑,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暖眼,沒想到半個(gè)月后惕耕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诫肠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年司澎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栋豫。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挤安,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丧鸯,到底是詐尸還是另有隱情蛤铜,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布丛肢,位于F島的核電站昂羡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏摔踱。R本人自食惡果不足惜虐先,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望派敷。 院中可真熱鬧蛹批,春花似錦、人聲如沸篮愉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽试躏。三九已至猪勇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颠蕴,已是汗流浹背泣刹。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留犀被,地道東北人椅您。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像寡键,于是被迫代替她去往敵國和親掀泳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353