摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達(dá)式相關(guān),就是切面如何切點。例子中,注解就是切點表達(dá)式,匹配對應(yīng)的連接點,通知,指在切面的某個特定的連接點上執(zhí)行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。
之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態(tài)代理嗎?看這篇就夠了》介紹了下JDK的動態(tài)代理。
基于這兩者的實現(xiàn)上,這次來探索下Spring的AOP原理。雖然AOP是基于Spring容器和動態(tài)代理,但不了解這兩者原理也絲毫不影響理解AOP的原理實現(xiàn),因為大家起碼都會用。
AOP,Aspect Oriented Programming,面向切面編程。在很多時候我們寫一些功能的時候,不需要用到繼承這么重的方法,例如對每個方法在執(zhí)行前打log,在沒有AOP的情況下,我們只能對每個方法都寫一句打log的語句。如果是一個復(fù)雜點的功能,那么將會產(chǎn)生許多重復(fù)的代碼,而且會對模塊之間有更多的耦合。
然而,在AOP下,我們只需要通過特定的方法,就能直接切入代碼,添加自定義的功能(后續(xù)再講AOP里面的概念點)。
下面將從一個簡單的示例入手,拆解示例的內(nèi)容,通過源碼分析,一步步帶大家讀懂AOP的原理實現(xiàn)。
使用示例以下代碼不以文字形式展示,若需要代碼,可以到github查看完整Demo。
Demo:https://github.com/Zack-Ku/sp...
Spring項目依然是用xml最原始的配置方式,為了只是能簡單地閱讀原理,否則會多很多自動配置的內(nèi)容在里面。而AOP的配置用的是注解形式,因為畢竟看起來畢竟清晰,容易理解邏輯。
創(chuàng)建一個Gradle項目,添加對應(yīng)的Spring與AOP的依賴。
(Gradle和Maven類似,都是自動化構(gòu)建的工具。但與Maven相比,Gradle是基于groovy,采用DSL格式,具有更強(qiáng)的靈活性、簡潔性、拓展性?,F(xiàn)在連Spring的官方源碼都是用Gradle的,可以說是一款面向未來的工具,后續(xù)也值得我們深入學(xué)習(xí)。)
創(chuàng)建一個Bean,TestBean。
創(chuàng)建AOP的Aspect。
然后寫一個啟動類,測試以上配置
運(yùn)行結(jié)果:
com.zack.demo.TestBean.getStr()開始執(zhí)行...
getStr():Testing!
com.zack.demo.TestBean.getStr()方法結(jié)束...
Demo:https://github.com/Zack-Ku/sp...
示例解析與AOP術(shù)語概念看到上面的結(jié)果,很容易猜想到,LogAspect作用了在TestBean上,使得每次執(zhí)行TestBean上的方法時,都會執(zhí)行對應(yīng)的方法(before/after)。
LogAspect中帶注解@Pointcut的allMethod(),是用來掃描程序中的連接點。當(dāng)執(zhí)行一個方法時,命中了連接點,則會根據(jù)不同的通知,執(zhí)行對應(yīng)的織入代碼。在上面例子中,執(zhí)行g(shù)etStr()前會執(zhí)行LogAspect中的before(),執(zhí)行g(shù)etStr()后會執(zhí)行LogAspect中的after()。
具體的通知包含
@Before,前置通知,執(zhí)行方法前執(zhí)行
@AfterReturn,返回通知,正常返回方法后執(zhí)行
@After,后置通知,方法最終結(jié)束后執(zhí)行,相當(dāng)于finaly
@Around,環(huán)繞通知,圍繞整個方法
@AfterThrowing,異常通知,拋出異常后執(zhí)行
開發(fā)者在命中連接點時,可以通過以上不同的通知,執(zhí)行對應(yīng)方法。這就是AOP中的Advisor。
以上的內(nèi)容其實已經(jīng)把AOP核心的概念都已經(jīng)點出來了,我們再深入具體的認(rèn)識下其中的術(shù)語,
Aspect,切面,一個關(guān)注點的模塊。
例子中,LogAspect就是切面。
JoinPoint, 連接點,程序執(zhí)行中的某個點,某個位置。
例子中,testBean.getStr()是連接點。
PointCut,切點,切面匹配連接點的點,一般與切點表達(dá)式相關(guān),就是切面如何切點。
例子中,@PointCut注解就是切點表達(dá)式,匹配對應(yīng)的連接點
Advice,通知,指在切面的某個特定的連接點上執(zhí)行的動作。
例子中,before()與after()方法中的代碼。
TargetObject,目標(biāo)對象,指被切入的對象。
例子中,從ctx中取出的testBean則是目標(biāo)對象。
Weave,織入,將Advice作用在JoinPoint的過程。
以上概念看起來可以還比較難懂,可以通過以下一圖(來源于網(wǎng)絡(luò))來理解
請各位讀者和各位程序員,在閱讀源碼的時候,一定要先搞清楚基本概念,和一定一定要知道對應(yīng)概念的英文,否則在看源碼的時候,根本對不上號,使理解難度大大提高。因為源碼都是英文寫的。
至此AOP的基本使用和概念相信大家都有一定的了解,下面開始從源碼入手,去探索整個Spring AOP的實現(xiàn)。
源碼分析上面的例子之所以能完成AOP的代理,只因為Spring的xml配置里面加了這一句
< aop : aspectj-autoproxy / >
加上了這一個配置,使得整個Spring項目擁有了AOP的功能。全局搜索下aspectj-autoproxy這個字段,可以發(fā)現(xiàn),是這個類AspectJAutoProxyBeanDefinitionParser解析了這個元素。
其中的parse方法調(diào)用的是AopNamespaceUtils類中的registerAspectJAnnotationAutoProxyCreatorIfNecessary。這個方法作用是初始化一個AOP專用的Bean,并且注冊到Spring容器中。
解析這三個操作,
第一句,注冊一個AnnotationAwareAspectJAutoProxyCreator(稱它為自動代理器),這個Creator是AOP的操作核心,也是掃描Bean,代理Bean的操作所在。
第二句,解析配置元素,決定代理的模式。其中有JDK動態(tài)代理,還有CGLIB代理,這部分后續(xù)會再細(xì)講。
第三句,作為系統(tǒng)組件,把Creator這個Bean,放到Spring容器中。讓Spring實例化,啟動這個Creator。
自動代理器下面我們來細(xì)看AnnotationAwareAspectJAutoProxyCreator是怎么對Bean做AOP的。
AnnotationAwareAspectJAutoProxyCreator的父類AbstractAutoProxyCreator,里面實現(xiàn)了BeanPostProceesor接口的postProcessAfterInitialization方法(該方法在一個Bean加載到Spring后會執(zhí)行)。
關(guān)聯(lián)注釋描述可知,當(dāng)一個bean加載完后,執(zhí)行了該方法,會生成一個新的代理對象,返回context中加載。
下面重點看其中的wrapIfNecessary方法。講述了整個AOP的核心流程,是Spring AOP最最最核心的代碼所在。
看到紅框的兩個核心方法,可以知道,先從剛加載的Bean中掃描出所有的advice和advisor,然后用它來創(chuàng)建一個代理對象。
先看如何掃描出advice和advisor。
一步步Debug getAdvicesAndAdvisorsForBean(),找到BeanFactoryAspectJAdvisorsBuilder中的buildAspectJAdvisors方法。
該方法就是找出Spring容器中存在的AspectBean,然后返回所有AspectBean中的Advisor。
示例中,LogAspect就是AspectBean,然后LogAspect中的before和after方法就是Advisor。
所以最終返回了LogAspect中的Advisor(before和after)。
拿到了所有的Advisor后,就進(jìn)入了創(chuàng)建代理的流程了createProxy()。
這些入?yún)ⅲ瑢Ρ壬弦黄v過的動態(tài)代理,其實非常相似。
beanClass,加載到Spring,觸發(fā)AOP的bean類
targetSource,目標(biāo)對象,示例中則是從ctx中取出的testBean
specificInterceptors,指定Advisor,示例中則是before和after的方法。
下面來具體看下代理的過程
代碼可以概括為,創(chuàng)建一個proxyFactory對象,然后把上面的參數(shù)都丟到這個這個工廠里,最后從proxyFactory獲取一個代理對象。
來看看ProxyFactory的getProxy方法是怎么生成代理對象的。
Debug該方法,可以在DefaultAopProxyFactory中createAopProxy看到
工廠會根據(jù)配置與目標(biāo)對象的類型,選擇用JDK動態(tài)代理(參考《你真的完全了解Java動態(tài)代理嗎?看這篇就夠了》)還是CGLIB的代理(CGLIB具體在后續(xù)講)。
代理后的對象放回ctx中,然后當(dāng)程序執(zhí)行的時候,會直接調(diào)用這個代理類。
至此整個AOP的代理流程就結(jié)束了。下面來了解下CGLIG代理與JDK代理的不同
CGLIB與JDK代理區(qū)別CGLIB(Code Generation Library)是一個強(qiáng)大的,高性能,高質(zhì)量的Code生成類庫。它可以在運(yùn)行期擴(kuò)展Java類與實現(xiàn)Java接口。Hibernate支持它來實現(xiàn)PO(Persistent Object 持久化對象)字節(jié)碼的動態(tài)生成。
回顧下JDK代理,JDK代理需要一組需要實現(xiàn)的接口,然后通過這些接口獲取構(gòu)造方法,用這個構(gòu)造方法和InvocationHandler,實例化一個對象出來。所以JDK的方式是基于接口的。
而CGLIB的代理是基于類的,用目標(biāo)類生成一個子類,子類重寫父類的方法,從而達(dá)到動態(tài)代理的效果。CGLIB的使用和實現(xiàn)等后面有機(jī)會再詳細(xì)介紹。目前暫時只要理解兩者不同的使用場景就足夠了。
總結(jié)回顧下Spring AOP的流程
Spring加載自動代理器AnnotationAwareAspectJAutoProxyCreator,當(dāng)作一個系統(tǒng)組件。
當(dāng)一個bean加載到Spring中時,會觸發(fā)自動代理器中的bean后置處理
bean后置處理,會先掃描bean中所有的Advisor
然后用這些Adviosr和其他參數(shù)構(gòu)建ProxyFactory
ProxyFactory會根據(jù)配置和目標(biāo)對象的類型尋找代理的方式(JDK動態(tài)代理或CGLIG代理)
然后代理出來的對象放回context中,完成Spring AOP代理
相信大家通過閱讀本文,對Spring的AOP處理有一定的認(rèn)識。想更深入地了解,探索每一步,每一行代碼的實現(xiàn),可以下載Demo源碼,一步步地調(diào)試
Demo:https://github.com/Zack-Ku/sp...
更多技術(shù)文章、精彩干貨,請關(guān)注
博客:zackku.com
微信公眾號:Zack說碼
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/77133.html
摘要:問題來了,我們到底還在用嗎答案是,不全用。后者是初始化的配置,主要是的配置。啟動類測試啟動項目后,在瀏覽器里面輸入。通過查詢已裝載的,并且支持該而獲取的。按照前面對的描述,對于而言,這個必定是。的核心在的方法中。 之前已經(jīng)分析過了Spring的IOC(《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》)與AOP(《從源碼入手,一文帶你讀懂Spring AOP面向切面編程》)的源碼,本次...
摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語言和等其他語言的對比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復(fù)實現(xiàn)故障恢復(fù)自動化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯過的技術(shù)要點大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...
摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語言和等其他語言的對比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復(fù)實現(xiàn)故障恢復(fù)自動化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯過的技術(shù)要點大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...
摘要:今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個記錄的過程讓我也有了新的理解。希望大家,收藏,點贊,加轉(zhuǎn)發(fā)。 今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個記錄的過程讓我也有了新的理解。希望大家,收藏,點贊,加轉(zhuǎn)發(fā)。 面試必備 面試必備:深入Spring MVC DispatchServlet 源碼...
閱讀 590·2023-04-26 00:33
閱讀 3612·2021-11-24 09:39
閱讀 3220·2021-09-22 15:34
閱讀 2429·2019-08-23 18:07
閱讀 2985·2019-08-23 18:04
閱讀 3838·2019-08-23 16:06
閱讀 2961·2019-08-23 15:27
閱讀 1670·2019-08-23 14:32