AOP是Spring Core中幾大重要能力之一职恳,我們可以使用AOP實(shí)現(xiàn)很多功能,比如我們常用的日志處理與Spring中的聲明式事務(wù)蛹屿。
AOP的幾個(gè)重要概念
Aspect:切面屁奏,在Spring中意為所有通知方法所在的類(lèi)
Join point:連接點(diǎn),程序執(zhí)行中的一點(diǎn)错负,在Spring中只表示方法執(zhí)行(Spring只支持方法級(jí)別的攔截)
Advice:通知坟瓢,在特定連接點(diǎn)上采取的操作,Spring將通知抽象為攔截器犹撒,并圍繞連接點(diǎn)維護(hù)攔截器鏈折联。共有5種類(lèi)型,before(切點(diǎn)之前執(zhí)行)识颊,around(環(huán)繞執(zhí)行诚镰,即切點(diǎn)前后執(zhí)行),After returning(切點(diǎn)正常執(zhí)行完返回后執(zhí)行)祥款,After throwing(切點(diǎn)拋出異常后執(zhí)行)清笨,after(切點(diǎn)之后執(zhí)行,不管是異沉ぃ或正常結(jié)束)函筋,AOP攔截器鏈則為以上五種通知組成。我們可以在通知方法中獲得我們需要的參數(shù)(返回值奠伪,異常信息跌帐,代理對(duì)象等)
Pointcut:切點(diǎn),與通知一起出現(xiàn)绊率,使用專(zhuān)門(mén)的切點(diǎn)表達(dá)式?jīng)Q定在何處執(zhí)行通知方法谨敛。
Introduction:引入,為類(lèi)添加新的方法或字段滤否。
Target object:被代理的對(duì)象
AOP proxy:AOP代理對(duì)象脸狸,由JDK動(dòng)態(tài)代理或CGLIB代理生成
Weaving:織入,將通知等織入代理類(lèi)藐俺。Spring AOP是動(dòng)態(tài)織入(運(yùn)行時(shí)織入)炊甲,AspectJ則是靜態(tài)織入(編譯時(shí)織入)
幾個(gè)注意點(diǎn)
關(guān)于Spring AOP的具體使用這里不做介紹,具體見(jiàn)文檔欲芹,這里說(shuō)幾個(gè)在使用代理時(shí)需要注意的地方卿啡。
由于Spring AOP框架基于代理的特性,目標(biāo)對(duì)象內(nèi)的調(diào)用根據(jù)定義不會(huì)被攔截菱父。自調(diào)用即類(lèi)似this.bar()或this.foo()這樣的調(diào)用颈娜,即使在bar方法上有通知方法通知也不會(huì)執(zhí)行。對(duì)于JDK代理浙宜,只能攔截代理上的公共接口方法調(diào)用官辽。使用CGLIB,可以攔截代理上的公共和受保護(hù)方法調(diào)用(Cglib基于子父類(lèi)實(shí)現(xiàn)代理粟瞬,而私有方法不會(huì)被子類(lèi)繼承)同仆。當(dāng)多個(gè)通知都想在同一個(gè)連接點(diǎn)上運(yùn)行時(shí),他們將按照優(yōu)先級(jí)順序執(zhí)行裙品,優(yōu)先級(jí)順序可以使用Order接口來(lái)定義乓梨。
總結(jié)一句:自調(diào)用通知方法不執(zhí)行,私有方法通知不執(zhí)行清酥。
原理
接下來(lái)從源碼角度分析下Spring AOP的實(shí)現(xiàn)原理扶镀。
在Spring中我們使用@EnableAspectJautoProxy開(kāi)啟AOP功能,我們以此為入口焰轻。(其他的Enable注解分析原理都是一樣的臭觉,比如EnableAsync等)。
它使用@Import注解導(dǎo)入了AspectJAutoProxyRegistrar類(lèi)辱志,該類(lèi)實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口蝠筑,用于向Spring中注冊(cè)類(lèi)。
在registerBeanDefinitions方法的第一行注冊(cè)了AOP需要的相關(guān)bean揩懒,方法中的下面部分是取EnableAspectJAutoProxy注解的信息什乙,根據(jù)參數(shù)值做相應(yīng)的處理,這里主要關(guān)注方法的首行代碼已球,進(jìn)入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法臣镣。
發(fā)現(xiàn)最終注冊(cè)了AnnotationAwareAspectJAutoProxyCreator辅愿。該類(lèi)是實(shí)現(xiàn)AOP的基礎(chǔ),我們對(duì)該類(lèi)進(jìn)行分析忆某,首先來(lái)看繼承結(jié)構(gòu)
BeanFactoryAware接口主要用于設(shè)置BeanFactory点待,這里我們主要關(guān)注InstantiationAwareBeanPostProcessor與BeanPostProcessor接口,實(shí)現(xiàn)這兩個(gè)接口意味著AnnotationAwareAspectJAutoProxyCreator是一個(gè)Spring的后置處理器弃舒,后置處理器會(huì)在bean的創(chuàng)建過(guò)程中起作用癞埠,關(guān)于后置處理器不熟悉的同學(xué)可以去看這篇文章Spring之IOC容器初始化。
InstantiationAwareBeanPostProcessor有一個(gè)before與after接口聋呢,由接口名可知兩個(gè)方法分別在bean實(shí)例化前后調(diào)用苗踪,關(guān)于Spring中bean的實(shí)例化過(guò)程不清楚的可以看Spring Bean的實(shí)例化分析。我們?cè)谧宇?lèi)中找到他們的實(shí)現(xiàn)(after方法由于沒(méi)有特別的處理這里就省略了)
首先從緩存中獲取削锰,然后調(diào)用this.isInfrastructureClass(beanClass)判斷創(chuàng)建的類(lèi)是否為Advice通铲、Pointcut等相關(guān)類(lèi),若是則放入adviseBean集合并返回null喂窟,正常的bean經(jīng)過(guò)該方法會(huì)返回null测暗,這里主要是用來(lái)處理我們的切面類(lèi)。
bean創(chuàng)建完成后接下來(lái)就是另一個(gè)接口BeanPostprocess(實(shí)例化磨澡,調(diào)用構(gòu)造函數(shù))開(kāi)始起作用了碗啄。
他會(huì)在InstantiationAwareBeanPostProcessor(初始化,即BeanDefination的初始化)接口方法執(zhí)行完之后調(diào)用稳摄,查看其實(shí)現(xiàn)(before方法由于沒(méi)有特別的處理這里就省略了)
最終會(huì)調(diào)用wrapIfNecessary方法判斷該bean是否需要增強(qiáng)稚字。進(jìn)入方法
正常bean的創(chuàng)建會(huì)進(jìn)入到isInfrastructureClass這個(gè)分支,isInfrastructureClass這個(gè)方法就是之前分析的判斷是否是Aspect等注解的類(lèi)厦酬,如果不是則調(diào)用getAdvicesAndAdvisorsForBean方法獲取到符合該bean的通知方法(即相應(yīng)的Advisor)胆描。
最終調(diào)用createProxy創(chuàng)建代理對(duì)象。
最終進(jìn)入代理工廠創(chuàng)建代理對(duì)象的方法仗阅,根據(jù)是否實(shí)現(xiàn)接口自動(dòng)選擇創(chuàng)建JDK動(dòng)態(tài)代理(基于接口)或者是Cglib代理(基于子父類(lèi))昌讲。到這里切面以及要被代理的類(lèi)就都創(chuàng)建完成了,接下來(lái)就是如何運(yùn)行通知方法了减噪。
執(zhí)行流程
我們這里假設(shè)上一步創(chuàng)建的對(duì)象為Cglib對(duì)象短绸,了解過(guò)Cglib代理的同學(xué)都知道實(shí)現(xiàn)代理要實(shí)現(xiàn)MethodInterceptor接口,在里面的intercept方法中進(jìn)行方法的攔截筹裕。我們找到代理類(lèi)的intercept方法
首先調(diào)用getInterceptorsAndDynamicInterceptionAdvice方法獲取所有通知方法的Advisor攔截器鏈醋闭,chain不為空會(huì)依次調(diào)用對(duì)應(yīng)的Advisor攔截器的proceed方法進(jìn)行代理調(diào)用,在此會(huì)按照通知的順序執(zhí)行原方法與通知方法朝卒。
大體的執(zhí)行流程就分析完了证逻,有時(shí)間的同學(xué)最好簡(jiǎn)單寫(xiě)個(gè)demo然后跟著一步步debug,這樣能夠更清晰的了解流程抗斤。
最后總結(jié)一下囚企,容器初始化時(shí)將切面等信息放入通知集合中丈咐,正常bean在創(chuàng)建時(shí)會(huì)判斷該bean是否需要被增強(qiáng),若需要增強(qiáng)洞拨,創(chuàng)建相應(yīng)的代理對(duì)象扯罐。在執(zhí)行時(shí)负拟,代理對(duì)象執(zhí)行相應(yīng)的invoke方法烦衣,在方法中獲取到通知集合并抽象成攔截器鏈,使用攔截器模式按照順序執(zhí)行相應(yīng)的方法掩浙。
附兩張流程圖: