spring aop代碼的增強(qiáng)

這篇博客,主要會(huì)分析spring aop是如何實(shí)現(xiàn)代碼增強(qiáng)的。

上一篇博客 我們大概知道,spring能在不改變代碼的前提下,往一個(gè)方法的之前和之后添加代碼。

想下,java中有哪種技術(shù)可以幫我們實(shí)現(xiàn)動(dòng)態(tài)修改代碼呢?就是jdk的動(dòng)態(tài)代理。關(guān)于動(dòng)態(tài)代理可以看下這篇博客jdk動(dòng)態(tài)代理與cglib動(dòng)態(tài)代理實(shí)現(xiàn)原理

大體我們先知道,jdk動(dòng)態(tài)代理和cglib的動(dòng)態(tài)代理都可以在運(yùn)行時(shí)修改源碼。兩者之間的基本區(qū)別之一是,jdk動(dòng)態(tài)代理需要被代理的類實(shí)現(xiàn)接口,cglib不需要。

然后我們開始分析spring是如何使用動(dòng)態(tài)代理的。

回到spring調(diào)用鏈的圖,spring 對(duì)于aop的增強(qiáng)是在創(chuàng)建bean時(shí),通過(guò)BeanPostProcessor機(jī)制來(lái)實(shí)現(xiàn)的。在spring初始化之后類后,會(huì)有一次對(duì)類增強(qiáng)的機(jī)會(huì)。就是在AbstractAutoProxyCreator里。

AbstractAutoProxyCreator.postProcessAfterInitialization

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

這里就是檢查下該類是否已經(jīng)暴露過(guò)了(可能已經(jīng)創(chuàng)建了,比如A依賴B時(shí),創(chuàng)建A時(shí)候,就會(huì)先去創(chuàng)建B。當(dāng)真正需要?jiǎng)?chuàng)建B時(shí),就沒必要再代理一次已經(jīng)代理過(guò)的對(duì)象)

wrapIfNecessary

真正創(chuàng)建代理對(duì)象的地方就在wrapIfNecessary


依舊我們先來(lái)看最關(guān)鍵的一步,createProxy

protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);
    }

    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    //獲取代理對(duì)象
    return proxyFactory.getProxy(getProxyClassLoader());
}

createProxy也很長(zhǎng),依舊我們只關(guān)心最重要的也就是最后一行,proxyFactory.getProxy

整理了一個(gè)流程圖



大概說(shuō)明下:
1)代理對(duì)象的創(chuàng)建最終委托給AopProxy生成
2)AopProxy的創(chuàng)建交由AopProxyFactory工廠
3)AopProxyFactory的默認(rèn)實(shí)現(xiàn)是DefaultAopProxyFactory

AopProxyFactory.createAopProxy

那么我們先來(lái)看看AopProxy究竟是如何創(chuàng)建的。


我們首先可以看出,最后生成的AopProxy底層實(shí)現(xiàn)要么是cglib的,要么是jdk。再然后,我們分析下,什么時(shí)候使用jdk動(dòng)態(tài)代理,什么時(shí)候使用cglib的。

1)配置了optimize。由于 cglib的動(dòng)態(tài)代理創(chuàng)建類比較慢,但是執(zhí)行代碼比較快,jdk動(dòng)態(tài)代理相反,創(chuàng)建比較快,執(zhí)行比較慢。如果配置了optimize=true,那么目標(biāo)類實(shí)現(xiàn)了接口就使用jdk代理,否則就用cglib。默認(rèn)是false。
2)proxyTargetClass 是否強(qiáng)制使用cglib實(shí)現(xiàn)代理。默認(rèn)是false
3)沒有可代理的接口

代理對(duì)象的生成

到了這里,我們繼續(xù)分析AopProxy如何生成代理對(duì)象(這里以jdk動(dòng)態(tài)代理來(lái)分析)



看到Proxy.newProxyInstance就非常熟悉了,jdk的動(dòng)態(tài)代理。

總結(jié)及待續(xù)

這篇博客大致分析了下,spring是如何創(chuàng)建代理對(duì)象的。jdk及cglib代理方式的選擇。以及大概看了下jdk代理對(duì)象的創(chuàng)建。

當(dāng)然還是留了很多問(wèn)題,首先就是wrapIfNecessary里的各種緩存究竟是干啥用的,再然后就是jdk代理里,非常核心的InvocationHandler.invoke方法是如何實(shí)現(xiàn)的。再然后,我們常說(shuō)的,切面,通知這些概念在源碼層究竟對(duì)應(yīng)著啥。這些問(wèn)題會(huì)再下一篇博客介紹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記,整理的知識(shí)點(diǎn),也是為了防止忘記,尊重勞動(dòng)成果,轉(zhuǎn)載注明出處哦!如果你也喜歡,那...
    波波波先森閱讀 12,440評(píng)論 6 86
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,255評(píng)論 6 342
  • 是不是你心里也有那么一個(gè)人,雖早已不再聯(lián)系,但每天都會(huì)想起,想忘卻記憶猶新,回憶卻滿是嘆息。 隨著年齡的增長(zhǎng) 人總...
    糖糖還叫棉花糖閱讀 278評(píng)論 0 0
  • 序 我最不喜歡敢的就是跟風(fēng)炒作,因?yàn)槲腋杏X我的生活不能掌握,我的選擇是被迫的,我嚴(yán)重懷疑被欺騙,即使事后證明我的選...
    一點(diǎn)點(diǎn)先生閱讀 351評(píng)論 0 0

友情鏈接更多精彩內(nèi)容