Spring AOP源碼解讀1 - 程序入口

前言

最近看了《從零開(kāi)始寫(xiě)JavaWeb框架》茁肠,想比較一下Spring AOP的實(shí)現(xiàn)方式和書(shū)的上實(shí)現(xiàn)方式有什么不同遭顶,所以先把Spring AOP的源碼讀一下瞒爬,再進(jìn)行比較瘾带。

Spring的源碼實(shí)在是復(fù)雜,在讀的過(guò)程中參考了很多書(shū)和網(wǎng)上的文章驻售,本文算是這些文章的總結(jié)露久,再加上一些我自己對(duì)另個(gè)細(xì)節(jié)的理解。

本文分成 3 部分:

  • 程序入口
  • 切面和增強(qiáng)的取得
  • 代理的生成

一欺栗,注冊(cè)AspectJAnnotationAutoProxyCreator

如果使用<aop:aspectj-autoproxy />標(biāo)簽來(lái)自動(dòng)生成代理的話毫痕,入口程序是AopNamespaceHandler征峦。在AopNamespaceHandler中,下面一段代碼是對(duì)<aop:aspectj-autoproxy />標(biāo)簽執(zhí)行的調(diào)用:

registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

AspectJAutoProxyBeanDefinitionParser解析器中消请,首先調(diào)用的parse方法栏笆。parse方法中有一行代碼:

AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);

我們看一下registerAspectJAnnotationAutoProxyCreatorIfNecessary方法的實(shí)際內(nèi)容:

public static void registerAutoProxyCreatorIfNecessary(
    ParserContext parserContext, Element sourceElement) {
    // 注冊(cè)或更新 AutoProxyCreator 定義 beanName 為 org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
    // 如果internalAutoProxyCreator的BeanDefinition已經(jīng)存在,而根據(jù)優(yōu)先級(jí)更新BeanDefinition
    // 在這里我們注冊(cè)的是AnnotationAwareAspectJAutoProxyCreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 對(duì)于 proxy-target-class 以及 expose-proxy 屬性的處理
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 注冊(cè)組件并通知梯啤,便于監(jiān)聽(tīng)器作進(jìn)一步處理
    // 其中 beanDefinition 的 className 為 AnnotationAwareAspectJAutoProxyCreator
    registerComponentIfNecessary(beanDefinition, parserContext);
}



那為什么注冊(cè)AnnotationAwareAspectJAutoProxyCreator竖伯,注冊(cè)AnnotationAwareAspectJAutoProxyCreator有什么用呢?

其實(shí),實(shí)現(xiàn)AOP處理是其實(shí)是通過(guò)BeanPostProcessor機(jī)制實(shí)現(xiàn)的因宇。AnnotationAwareAspectJAutoProxyCreator的父類(lèi)也實(shí)現(xiàn)一個(gè)BeanPostProcessor類(lèi)型的接口七婴,而生成代理的邏輯就在AnnotationAwareAspectJAutoProxyCreator的BeanPostProcessor接口實(shí)現(xiàn)里面。
更嚴(yán)謹(jǐn)?shù)卣f(shuō)察滑,AnnotationAwareAspectJAutoProxyCreator的父類(lèi)實(shí)現(xiàn)的接口是
SmartInstantiationAwareBeanPostProcessor打厘,主要是Spring框架內(nèi)部使用的一個(gè)接口。而這個(gè)接口的父接口InstantiationAwareBeanPostProcessor 是實(shí)現(xiàn)代理的重點(diǎn)之一贺辰。

這3個(gè)接口的關(guān)系如下:
SmartInstantiationAwareBeanPostProcessor -> InstantiationAwareBeanPostProcessor -> BeanPostProcessor

為什么說(shuō)是是InstantiationAwareBeanPostProcessor接口的子接口户盯,接口是重點(diǎn)之一?那InstantiationAwareBeanPostProcessor接口是什么接口呢饲化?

BeanPostProcessor主要作用于Bean實(shí)例化后莽鸭,初始化前后。InstantiationAwareBeanPostProcessor雖然是BeanPostProcessor的子接口吃靠,但它的調(diào)用時(shí)間點(diǎn)其發(fā)生在Bean實(shí)例化前硫眨,在真正調(diào)用doCreate()創(chuàng)建bean實(shí)例之前。
在創(chuàng)建Bean實(shí)例之前巢块,會(huì)先調(diào)用resolveBeforeInstantiation方法礁阁,這個(gè)方法是生成Bean代理的地方。如果此方法返回值不為空則直接返回生成的Bean的代理族奢,如果為空就向下走正常的Bean生成流程姥闭。

spring注釋“Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. ”給BeanPostProcessors一個(gè)機(jī)會(huì)返回代理proxy對(duì)象。

InstantiationAwareBeanPostProcessor接口方法越走,就是在resolveBeforeInstantiation方法中調(diào)用的棚品。所以可以看出,BeanPostProcessore有很多廊敌,但Spring AOP的實(shí)現(xiàn)就是通過(guò)InstantiationAwareBeanPostProcessor這個(gè)BeanPostProcessor實(shí)現(xiàn)的南片。看一下源碼:

protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
        throws BeanCreationException {
    ......
    try {
        // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        // 如果返回值不為空庭敦,說(shuō)明生成成了此BeanName的代理,直接返回代理對(duì)象
        Object bean = resolveBeforeInstantiation(beanName, mbd);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        ......
    }

    // 如果沒(méi)有生成代理對(duì)象薪缆,就按正常流程走秧廉,生成Bean對(duì)象
    Object beanInstance = doCreateBean(beanName, mbd, args);
    .....
    return beanInstance;
}
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                // 調(diào)用InstantiationAwareBeanPostProcessor接口的地方
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName)
            throws BeansException {

        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                // 轉(zhuǎn)換成InstantiationAwareBeanPostProcessor接口伞广,并調(diào)用
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

順帶說(shuō)一下,注冊(cè)AnnotationAwareAspectJAutoProxyCreator的目的是:把這個(gè)類(lèi)的BeanDefinition通過(guò)registerBeanDefinition方法(DefaultListableBeanFactory類(lèi)中)加入到beanDefinitionMap中疼电,作為一個(gè)Bean讓Spring管理嚼锄,這樣Spring就可以隨意取得它了。



如果使用<aop:aspectj-autoproxy />標(biāo)簽來(lái)自動(dòng)生成代理的話蔽豺,入口程序是AopNamespaceHandler区丑。那AopNamespaceHandler 是在什么地方被調(diào)用的呢?
這個(gè)問(wèn)題讓我們從容器啟動(dòng)的地方開(kāi)始說(shuō)明修陡。以FileSystemXmlApplicationContext 為例沧侥,這個(gè)類(lèi)的入口是構(gòu)造函數(shù)里面的refresh() 方法。從refresh() 方法開(kāi)始魄鸦,調(diào)用流程是這樣的:(以下流程全部是嵌套調(diào)用的關(guān)系)

1. refresh() ->
   刷新容器 
2. obtainFreshBeanFactory() ->
   獲得刷新后的Bean容器
3. refreshBeanFactory() ->
   刷新Bean容器
4. loadBeanDefinitions() ->
   加載BeanDefinition
5. XmlBeanDefinitionReader#loadBeanDefinitions
   新建一個(gè)XmlBeanDefinitionReader實(shí)例(new XmlBeanDefinitionReader(beanFactory))宴杀,調(diào)用這個(gè)實(shí)例的loadBeanDefinitions方法。
6. XmlBeanDefinitionReader#doLoadBeanDefinitions
7. XmlBeanDefinitionReader#registerBeanDefinitions
8. XmlBeanDefinitionReader#createBeanDefinitionDocumentReader
   在這個(gè)方法中取得了DefaultBeanDefinitionDocumentReader實(shí)例拾因。接下來(lái)調(diào)用這個(gè)實(shí)例的方法旺罢。
9. DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
   在這個(gè)方法中,根據(jù)URI判斷是否使用AopNamespaceHandler
10. DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
   從registerBeanDefinitions方法開(kāi)始绢记,內(nèi)部連續(xù)調(diào)用一系列方法扁达,一直調(diào)用到parseBeanDefinitions方法。在這個(gè)方法中蠢熄,根據(jù)XML文件的URI判斷使用哪些解析器跪解,例如是<Beans>類(lèi)標(biāo)簽解析器,還是<aop>類(lèi)標(biāo)簽解析器护赊。
   如果是需要<aop>標(biāo)簽解析器的話惠遏,在this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)代碼中,從配置文件(spring.handlers)從取出AopNamespaceHandler的類(lèi)名骏啰,生成這個(gè)類(lèi)的實(shí)例节吮,然后調(diào)用這個(gè)類(lèi)的parse方法。
11. AopNamespaceHandler#parse
   這個(gè)方法的功能是判耕,根據(jù)具體標(biāo)簽調(diào)用具體解析器的parse方法透绩。
   - <aop:aspectj-autoproxy>:AspectJAutoProxyBeanDefinitionParser
   - <aop:config>:ConfigBeanDefinitionParser
   等。
   這個(gè)方法的調(diào)用壁熄,又回到了我們最初講的AopNamespaceHandler入口的地方帚豪。

到此為止,從容器到AopNamespaceHandler類(lèi)調(diào)用的過(guò)程也講完了草丧。
            

二狸臣,AspectJAnnotationAutoProxyCreator的流程

通過(guò)上面的內(nèi)容,我們知道了注冊(cè)AnnotationAwareAspectJAutoProxyCreator的意義昌执,并且知道了生成代理是在它的BeanPostProcessor接口里做的烛亦,現(xiàn)在看看被實(shí)現(xiàn)的接口的內(nèi)容诈泼。(postProcessAfterInitialization 具體實(shí)現(xiàn)是在其父類(lèi) AbstractAutoProxyCreator 中完成的):

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if (bean != null) {
      // 根據(jù)給定的 bean 的 class 和 name 構(gòu)建出個(gè) key,格式:beanClassName_beanName
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         // 如果它適合被代理煤禽,則需要封裝指定 bean铐达。
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   // 是否已經(jīng)處理過(guò)
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   // 無(wú)需增強(qiáng)
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   // 給定的 bean 類(lèi)是否代表一個(gè)基礎(chǔ)設(shè)施類(lèi),基礎(chǔ)設(shè)施類(lèi)不應(yīng)代理檬果,或者配置了指定 bean 不需要自動(dòng)代理
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // 如果存在增強(qiáng)方法則創(chuàng)建代理(*重要*)
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   // 如果獲取到了增強(qiáng)則需要針對(duì)增強(qiáng)創(chuàng)建代理
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      // 創(chuàng)建代理(*重要*)
      Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

這里有兩個(gè)方法特別重要:

  1. getAdvicesAndAdvisorsForBean:如果Bean是要被代理的對(duì)象的話瓮孙,取得Bean相關(guān)的Interceptor
  2. createProxy:創(chuàng)建代理

下一篇文章,我們就這兩個(gè)方法的下面的流程分開(kāi)來(lái)分析一下选脊,首先分析getAdvicesAndAdvisorsForBean相關(guān)代碼杭抠。




關(guān)于接口實(shí)現(xiàn)的補(bǔ)充:

AnnotationAwareAspectJAutoProxyCreator 一共實(shí)現(xiàn)了2個(gè)BeanPostProcessor 的接口,的4個(gè)方法:

  • postProcessBeforeInstantiation(InstantiationAwareBeanPostProcessor)
  • postProcessAfterInstantiation(InstantiationAwareBeanPostProcessor)
  • postProcessBeforeInitialization(BeanPostProcessor)
  • postProcessAfterInitialization(BeanPostProcessor)

postProcessBeforeInstantiationInstantiationAwareBeanPostProcessor接口)方法中知牌,這個(gè)方法是在AnnotationAwareAspectJAutoProxyCreator的基類(lèi)AbstractAutoProxyCreator中實(shí)現(xiàn)的祈争。細(xì)看一下,在postProcessAfterInitialization方法中也有類(lèi)似的生成代理的代碼角寸。這是為什么呢菩混?

上網(wǎng)找了一些資料,在postProcessBeforeInstantiation方法中有一個(gè)判斷:

如果某個(gè)Bean設(shè)置了自定義TargetSource的話扁藕,就在本方法中進(jìn)行生成代理

postProcessAfterInitialization則沒(méi)有這樣的判斷沮峡,只是在生成代理前判斷了一下代理是否已經(jīng)生成。具體為什么有這樣的必須還不清楚(以后有需要調(diào)查一下)亿柑,但結(jié)果就是:

  • 如果Bean設(shè)置了自定義TargetSource邢疙,就在postProcessBeforeInstantiation中生成代理
  • 如果沒(méi)有,就在postProcessAfterInitialization中生成代理望薄。

最后疟游,不管理在哪個(gè)方法里生成代理,在創(chuàng)建每個(gè)Bean時(shí)都會(huì)被調(diào)用這兩個(gè)方法痕支,代理的生成邏輯就是在這兩個(gè)方法中實(shí)現(xiàn)的颁虐。

關(guān)于TargetSource:spring-aop組件詳解——TargetSource目標(biāo)源
關(guān)于自定義TargetSource:《Spring揭密》的9.6章節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市卧须,隨后出現(xiàn)的幾起案子另绩,更是在濱河造成了極大的恐慌,老刑警劉巖花嘶,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笋籽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡椭员,警方通過(guò)查閱死者的電腦和手機(jī)车海,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)隘击,“玉大人侍芝,你說(shuō)我怎么就攤上這事喘沿。” “怎么了竭贩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)莺禁。 經(jīng)常有香客問(wèn)我留量,道長(zhǎng),這世上最難降的妖魔是什么哟冬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任楼熄,我火速辦了婚禮,結(jié)果婚禮上浩峡,老公的妹妹穿的比我還像新娘可岂。我一直安慰自己,他們只是感情好翰灾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布缕粹。 她就那樣靜靜地躺著,像睡著了一般纸淮。 火紅的嫁衣襯著肌膚如雪平斩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天咽块,我揣著相機(jī)與錄音绘面,去河邊找鬼。 笑死侈沪,一個(gè)胖子當(dāng)著我的面吹牛揭璃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亭罪,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼瘦馍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了皆撩?” 一聲冷哼從身側(cè)響起扣墩,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扛吞,沒(méi)想到半個(gè)月后呻惕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滥比,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年亚脆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盲泛。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡濒持,死狀恐怖键耕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柑营,我是刑警寧澤屈雄,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站官套,受9級(jí)特大地震影響酒奶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奶赔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一惋嚎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧站刑,春花似錦另伍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至玻靡,卻和暖如春结榄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背囤捻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工臼朗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蝎土。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓视哑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親誊涯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挡毅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)暴构,斷路器跪呈,智...
    卡卡羅2017閱讀 134,637評(píng)論 18 139
  • 什么是Spring Spring是一個(gè)開(kāi)源的Java EE開(kāi)發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,445評(píng)論 1 133
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,778評(píng)論 6 342
  • Spring簡(jiǎn)介 spring框架由Rod Johnson開(kāi)發(fā)取逾,2004年發(fā)布了Spring框架的第一版耗绿。Spri...
    qiuqiu_hz閱讀 1,077評(píng)論 0 15
  • 1.項(xiàng)目基本算是完成了 但遺留了兩個(gè)bug 明天解決 做項(xiàng)目時(shí)"小"錯(cuò)誤? a.接口文檔未細(xì)致看 b.單詞錯(cuò)誤 c...
    小小太陽(yáng)000閱讀 244評(píng)論 0 0