從dubbo內(nèi)核分析IOC&AOP

“載營(yíng)魄抱一臼闻,能無(wú)離乎?
專氣致柔今膊,能如嬰兒乎些阅?
滌除玄覽,能無(wú)疵乎斑唬?
愛(ài)民治國(guó),能無(wú)以智乎黎泣?
天門開(kāi)闔恕刘,能為雌乎?
明白四達(dá)抒倚,能無(wú)為乎褐着?
生之畜之,生而不有托呕,為而不恃含蓉,長(zhǎng)而不宰频敛,是謂玄德∠诳郏”[1]

Spring從誕生至今斟赚,ioc和aop一直都是其靈魂所在,至今我們使用spring依然依賴這兩大特性:依賴注入(ioc)差油、面向切面編程(aop)拗军。從設(shè)計(jì)模式上來(lái)講,卻極其簡(jiǎn)單蓄喇,無(wú)外乎工廠模式和代理模式发侵。
今天我們從dubbo的內(nèi)核源碼入手,來(lái)看看ioc和aop是如何影響著dubbo的框架設(shè)計(jì)妆偏。

ioc在dubbo中的應(yīng)用

dubbo的ioc主要體現(xiàn)在dubbo擴(kuò)展點(diǎn)加載的環(huán)節(jié)刃鳄,T injectExtension(T instance)方法中。該方法只在三個(gè)地方被使用:

createAdaptiveExtension()
 --injectExtension((T) getAdaptiveExtensionClass().newInstance()) //為創(chuàng)建好的AdaptiveExtensionClass實(shí)例進(jìn)行屬性注入
 
createExtension(String name)
 --injectExtension(instance) //為創(chuàng)建好的Extension實(shí)例進(jìn)行屬性注入
 --injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)) //為創(chuàng)建好的wrapperClass實(shí)例進(jìn)行屬性注入

ExtensionLoader:

    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())) {//一個(gè)參數(shù)的public的setXXX(T param)方法.例如,setName(String name)
                        Class<?> pt = method.getParameterTypes()[0];//參數(shù)param的類型T,eg.String
                        try {
                            String property = method.getName().length() > 3
                                ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";//獲取屬性名XXX, eg.name
                            Object object = objectFactory.getExtension(pt, property);//實(shí)例化參數(shù)
                            if (object != null) {
                                //執(zhí)行instance.method(object)方法,這里就是執(zhí)行instance的setter方法,進(jìn)行setter注入
                                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;
    }

整個(gè)方法的作用就是通過(guò)instance對(duì)象實(shí)例的setter方法為instance的屬性賦值钱骂,完成setter注入叔锐,即IOC的最經(jīng)典的注入方式。

詳細(xì)步驟:

  • 獲取instance的setter方法罐柳,通過(guò)setter方法獲取屬性名稱property和屬性類型pt(即paramType的簡(jiǎn)寫)
  • 使用objectFactory創(chuàng)建一個(gè)property名稱(類型為pt)的對(duì)象實(shí)例
  • 執(zhí)行instance的setter方法掌腰,注入property實(shí)例

其中,比較重要的就是:Object object = objectFactory.getExtension(pt, property);這個(gè)方法。其中的objectFactory=AdaptiveExtensionFactory實(shí)例从媚,其屬性factories = [SpringExtensionFactory實(shí)例, SpiExtensionFactory實(shí)例]泞坦。

看一下源碼:

private final List<ExtensionFactory> factories;

    public <T> T getExtension(Class<T> type, String name) {
        /**
         * 先調(diào)用SpiExtensionFactory來(lái)實(shí)例化;
         * 如果不行,再使用SpringExtensionFactory來(lái)實(shí)例化
         */
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

SpiExtensionFactory:

public class SpiExtensionFactory implements ExtensionFactory {
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {//type是接口且必須具有@SPI注解
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (loader.getSupportedExtensions().size() > 0) {//獲取type的所有ExtensionClasses實(shí)現(xiàn)的key
                return loader.getAdaptiveExtension();//獲取type的裝飾類,如果有@Adaptive注解的類,則返回該類的實(shí)例,否則返回一個(gè)動(dòng)態(tài)代理類的實(shí)例(例如Protocol$Adpative的實(shí)例)
            }
        }
        return null;
    }
}

從這里我們可以看出dubbo-SPI的另外一個(gè)好處:可以為SPI實(shí)現(xiàn)類注入SPI的裝飾類或動(dòng)態(tài)代理類。

SpringExtensionFactory:

public class SpringExtensionFactory implements ExtensionFactory {
    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);
    }

    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {
        for (ApplicationContext context : contexts) {
            if (context.containsBean(name)) {//該context是否包含name的bean
                Object bean = context.getBean(name);//獲取name的bean,如果是懶加載或多例的bean,此時(shí)會(huì)實(shí)例化name的bean
                if (type.isInstance(bean)) {//如果obj的類型是type或其子類,與instanceof相同
                    return (T) bean;
                }
            }
        }
        return null;
    }
}

aop在dubbo中的應(yīng)用

dubbo在獲取擴(kuò)展點(diǎn)時(shí)勺择,會(huì)有如下調(diào)用:

final Protocol dubboProtocol = loader.getExtension("dubbo");
調(diào)用層級(jí):

ExtensionLoader<T>.getExtension()
--createExtension(String name)
----getExtensionClasses().get(name)//獲取擴(kuò)展類
------loadExtensionClasses()
--------loadFile(Map<String, Class<?>> extensionClasses, String dir)
----injectExtension(instance);//ioc
----wrapper包裝;//aop

createExtension(String name),該方法源碼如下:

private T createExtension(String name) {
        /** 從cachedClasses緩存中獲取所有的實(shí)現(xiàn)類map,之后通過(guò)name獲取到對(duì)應(yīng)的實(shí)現(xiàn)類的Class對(duì)象 */
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            /** 從EXTENSION_INSTANCES緩存中獲取對(duì)應(yīng)的實(shí)現(xiàn)類的Class對(duì)象,如果沒(méi)有,直接創(chuàng)建,之后放入緩存 */
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                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);
        }
    }

這里伦忠,先給出META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol內(nèi)容:

registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol

com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper和com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper省核,這兩個(gè)類不含有@Adaptive注解且具有含有Protocol的單參構(gòu)造器,符合這樣條件的會(huì)被列入AOP增強(qiáng)類昆码。放置在loader的私有屬性cachedWrapperClasses中气忠。

此時(shí)的loader:

  • Class<?> type = interface com.alibaba.dubbo.rpc.Protocol
  • ExtensionFactory objectFactory = AdaptiveExtensionFactory(適配類)
    factories = [SpringExtensionFactory實(shí)例, SpiExtensionFactory實(shí)例]
  • cachedWrapperClasses = [class ProtocolListenerWrapper, class ProtocolFilterWrapper]

再來(lái)看createExtension(String name)中的紅色部分,就是今天的重點(diǎn)AOP赋咽。如上所講旧噪,我在cachedWrapperClasses中緩存了兩個(gè)AOP增強(qiáng)類:class ProtocolListenerWrapper和class ProtocolFilterWrapper。

首先是獲取ProtocolListenerWrapper的單參構(gòu)造器脓匿,然后創(chuàng)建ProtocolListenerWrapper實(shí)例淘钟,最后完成對(duì)ProtocolListenerWrapper實(shí)例進(jìn)行屬性注入,注意此時(shí)的instance=ProtocolListenerWrapper實(shí)例陪毡,而不再是之前的DubboProtocol實(shí)例了米母。之后使用ProtocolFilterWrapper以同樣的方式進(jìn)行包裝勾扭,只是此時(shí)ProtocolFilterWrapper包裝的是ProtocolListenerWrapper實(shí)例。


  1. 老子《道德經(jīng)》第十章铁瞒,老子故里妙色,中國(guó)鹿邑。 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末精拟,一起剝皮案震驚了整個(gè)濱河市燎斩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜂绎,老刑警劉巖栅表,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異师枣,居然都是意外死亡怪瓶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門践美,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)洗贰,“玉大人,你說(shuō)我怎么就攤上這事陨倡×沧蹋” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵兴革,是天一觀的道長(zhǎng)绎晃。 經(jīng)常有香客問(wèn)我,道長(zhǎng)杂曲,這世上最難降的妖魔是什么庶艾? 我笑而不...
    開(kāi)封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮擎勘,結(jié)果婚禮上咱揍,老公的妹妹穿的比我還像新娘。我一直安慰自己棚饵,他們只是感情好煤裙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著噪漾,像睡著了一般积暖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怪与,一...
    開(kāi)封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音缅疟,去河邊找鬼分别。 笑死遍愿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的耘斩。 我是一名探鬼主播沼填,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼括授!你這毒婦竟也來(lái)了坞笙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤荚虚,失蹤者是張志新(化名)和其女友劉穎薛夜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體版述,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梯澜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渴析。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晚伙。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖俭茧,靈堂內(nèi)的尸體忽然破棺而出咆疗,到底是詐尸還是另有隱情,我是刑警寧澤母债,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布午磁,位于F島的核電站,受9級(jí)特大地震影響场斑,放射性物質(zhì)發(fā)生泄漏漓踢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一漏隐、第九天 我趴在偏房一處隱蔽的房頂上張望喧半。 院中可真熱鬧,春花似錦青责、人聲如沸挺据。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扁耐。三九已至,卻和暖如春产阱,著一層夾襖步出監(jiān)牢的瞬間婉称,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留王暗,地道東北人悔据。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俗壹,于是被迫代替她去往敵國(guó)和親科汗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前面我們了解過(guò)了Java的SPI擴(kuò)展機(jī)制绷雏,對(duì)于Java擴(kuò)展機(jī)制的原理以及優(yōu)缺點(diǎn)也有了大概的了解头滔,這里繼續(xù)深入一下D...
    加大裝益達(dá)閱讀 5,062評(píng)論 2 20
  • 總結(jié):Protocolrefprotocol = ExtensionLoader.getExtensionLoad...
    Ngcc閱讀 705評(píng)論 0 2
  • 到底什么是詩(shī)歌,詩(shī)歌到底需要具備那些條件涎显? 現(xiàn)代已經(jīng)是現(xiàn)代坤检,所以今天不談古體詩(shī),只談現(xiàn)代詩(shī)部分棺禾。 這幾乎讓人郁悶缀蹄,...
    陌上冷閱讀 358評(píng)論 0 2
  • 左手生活悬襟,右手生存衅码。我的夢(mèng)想與現(xiàn)實(shí) 同事發(fā)來(lái)周末早上7點(diǎn)義務(wù)加班的抱怨,又發(fā)來(lái)一組她的朋友在西班牙旅行生活的美圖脊岳,...
    Sueshuya閱讀 1,339評(píng)論 12 6