- Spring 5企業(yè)級開發(fā)實(shí)戰(zhàn)
- 周冠亞 黃文毅
- 1727字
- 2019-11-22 18:47:12
3.6 Spring AOP的實(shí)現(xiàn)原理
Spring AOP的實(shí)現(xiàn)是通過創(chuàng)建目標(biāo)對象的代理類,并對目標(biāo)對象進(jìn)行攔截來實(shí)現(xiàn)的。分析Spring AOP的底層實(shí)現(xiàn),需要重點(diǎn)分析幾個(gè)常用類,相關(guān)類圖如3-15所示。
ProxyConfig類是一個(gè)基類——數(shù)據(jù)類,主要為各種AOP代理工廠提供屬性配置。
AdvisedSupport類是ProxyConfig類的子類,其封裝了AOP中對通知(Advice)和通知器(Advisor)的相關(guān)操作,這些操作對于不同的創(chuàng)建代理對象的類都是相同的,但是對于具體的AOP代理對象的生成需要AdvisedSupport各個(gè)子類去實(shí)現(xiàn)。

圖3-15 Spring AOP核心類圖
ProxyCreatorSupport類是AdvisedSupport的子類——輔助類,不同子類的一些通用的操作都封裝在ProxyCreatorSupport中。
ProxyFactoryBean,ProxyFactory和AspectJProxyFactory是用于創(chuàng)建AOP代理對象的,這三個(gè)類的作用分別如下:
? ProxyFactoryBean類:功能是創(chuàng)建聲明式的代理對象。
? ProxyFactory類:功能是創(chuàng)建編程式的代理對象。
? AspectJProxyFactory類:功能是創(chuàng)建基于AspectJ的代理對象。
3.6.1 設(shè)計(jì)原理
下面以ProxyFactoryBean為例,分析Spring AOP的實(shí)現(xiàn)原理。
首先定義一個(gè)接口Log,其中包含一個(gè)printLog()方法,Log接口的代碼如下:

再創(chuàng)建一個(gè)Target類,實(shí)現(xiàn)Log接口,重寫printLog方法,Target類的代碼如下:

然后創(chuàng)建一個(gè)通知類LogAroundAdvice并實(shí)現(xiàn)MethodInterceptor接口,重寫invoke()方法,在方法執(zhí)行前后分別打印實(shí)現(xiàn),LogAroundAdvice的實(shí)現(xiàn)如下:

創(chuàng)建一個(gè)測試類,用于觀察測試結(jié)果,測試代碼如下:

文件spring-chapter3-sourcecodelearning.xml的配置如下:

運(yùn)行測試代碼,測試結(jié)果如下:
方法執(zhí)行開始時(shí)間:2018-10-03 08:18:51 167 執(zhí)行一些操作 方法執(zhí)行結(jié)束時(shí)間:2018-10-03 08:18:52 172
從測試結(jié)果可以看出,在正常的調(diào)用printLog()方法前后分別打印了日志,說明AOP已經(jīng)實(shí)現(xiàn)了。下面將通過這個(gè)案例分析ProxyFactoryBean的實(shí)現(xiàn)邏輯。
打開ProxyFactoryBean的代碼,其生成代理對象的核心方法是getObject()方法,部分代碼如下:

下面分析getObject()方法中的initializeAdvisorChain()方法,initializeAdvisorChain()方法是初始化通知器鏈(或者叫攔截器鏈)的,其代碼如下:

執(zhí)行initializeAdvisorChain()方法后,如果是單例模式,將會(huì)調(diào)用getSingletonInstance()方法獲取一個(gè)單例模式的代理對象,getSingletonInstance()方法代碼如下:

執(zhí)行initializeAdvisorChain()方法后,如果是非單例模式即原型模式,將會(huì)調(diào)用newPrototypeInstance()方法獲取一個(gè)新的原型模式的代理對象,newPrototypeInstance()方法代碼如下:

可以發(fā)現(xiàn),無論是單例模式還是原型模式,最終都是通過調(diào)用getProxy()方法獲取代理對象的,getProxy()的實(shí)現(xiàn)如下:

通過以上對getSingletonInstance()方法和newPrototypeInstance()方法的代碼注釋可以發(fā)現(xiàn),這兩個(gè)方法都會(huì)調(diào)用ProxyCreatorSupport.createAopProxy()方法,ProxyCreatorSupport類的核心代碼如下:

從createAopProxy()方法的代碼可以看出,AopProxy對象是在DefaultAopProxyFactory類的createAopProxy()方法中生成的,DefaultAopProxyFactory.createAopProxy()方法的代碼如下:

從DefaultAopProxyFactory.createAopProxy()使用的類的名稱可以發(fā)現(xiàn),如果是繼承了接口的類,會(huì)使用JDK動(dòng)態(tài)代理,即用JdkDynamicAopProxy類創(chuàng)建代理對象,否則將會(huì)使用CGLIB動(dòng)態(tài)代理即用ObjenesisCglibAopProxy類創(chuàng)建代理對象,關(guān)于這兩種動(dòng)態(tài)代理的具體使用,請參考本章3.1節(jié)。
3.6.2 JdkDynamicAopProxy
JDK動(dòng)態(tài)代理只能針對接口起作用,Spring中通過JdkDynamicAopProxy類使用JDK動(dòng)態(tài)代理創(chuàng)建AOPProxy對象,JdkDynamicAopProxy類的定義如下:

JdkDynamicAopProxy類實(shí)現(xiàn)了InvocationHandler接口,因而可以使用JDK動(dòng)態(tài)代理產(chǎn)生代理對象。

此處的getProxy()方法是獲取代理對象的入口,其是通過調(diào)用以下方法實(shí)現(xiàn)的:

findDefinedEqualsAndHashCodeMethods()方法的功能是查找代理的接口是否有定義equals()或hashCode()方法。

通過在3.1節(jié)中的介紹可以得知,InvocationHandler接口的invoke()方法是代理對象執(zhí)行方法調(diào)用和增強(qiáng)的地方,下面分析JdkDynamicAopProxy實(shí)現(xiàn)InvocationHandler接口重寫invoke()方法的代碼:

通過以上代碼分析可知,最核心的功能都是在invocation.proceed()方法中實(shí)現(xiàn)的,下面分析ReflectiveMethodInvocation,代碼如下:

invokeJoinpoint()方法是調(diào)用目標(biāo)對象方法的地方,其實(shí)現(xiàn)如下:

invokeJoinpoint()方法會(huì)調(diào)用AopUtils.invokeJoinpointUsingReflection()方法,代碼如下:

可以看到invokeJoinpointUsingReflection()方法最終是通過反射調(diào)用目標(biāo)對象的方法。
通過對JdkDynamicAopProxy類的代碼進(jìn)行分析可以知道,JdkDynamicAopProxy類實(shí)現(xiàn)了InvocationHandler接口,重寫了invoke()方法,當(dāng)進(jìn)行調(diào)用時(shí),其實(shí)并不是調(diào)用目標(biāo)對象,而是為目標(biāo)對象創(chuàng)建一個(gè)代理對象,觸發(fā)代理對象的invoke()方法,在invoke()方法中會(huì)通過反射調(diào)用目標(biāo)對象的方法,Spring AOP相關(guān)通知的調(diào)用也是在invoke()方法中完成的。
3.6.3 CglibAopProxy
由于JDK動(dòng)態(tài)代理只能針對接口生成代理對象,對于沒有實(shí)現(xiàn)接口的目標(biāo)對象,需要使用CGLIB產(chǎn)生代理對象,下面分析CglibAopProxy的代碼。
回到DefaultAopProxyFactory.createAopProxy()方法,如果目標(biāo)對象沒有實(shí)現(xiàn)接口,將會(huì)返回一個(gè)ObjenesisCglibAopProxy對象。ObjenesisCglibAopProxy類的代碼如下:

從代碼可以看出,ObjenesisCglibAopProxy繼承了CglibAopProxy,Objenesis是一個(gè)輕量級的Java庫,作用是繞過構(gòu)造器創(chuàng)建一個(gè)實(shí)例。因此分析的重點(diǎn)還是CglibAopProxy類。

由本章3.1節(jié)可知,CGLIB的運(yùn)行需要配合回調(diào)方法,實(shí)現(xiàn)MethodInterceptor接口,在CglibAopProxy中也是一樣,下面分析獲取回調(diào)方法getCallbacks()的代碼:


通過上面對CGLIB創(chuàng)建代理和獲取回調(diào)通知的代碼分析,可以了解到CGLIB在獲取代理通知時(shí),會(huì)創(chuàng)建DynamicAdvisedInterceptor類;當(dāng)調(diào)用目標(biāo)對象的方法時(shí),不是直接調(diào)用目標(biāo)對象,而是通過CGLIB創(chuàng)建的代理對象來調(diào)用目標(biāo)對象;并且在調(diào)用目標(biāo)對象的方法時(shí),會(huì)觸發(fā)DynamicAdvisedInterceptor的intercept回調(diào)方法對目標(biāo)對象進(jìn)行處理,CGLIB回調(diào)攔截器鏈的代碼如下:


這里的CglibMethodInvocation類繼承了ReflectiveMethodInvocation類,CglibMethodInvocation.procceed()調(diào)用了父類的ReflectiveMethodInvocation.proceed()方法,和3.6.2節(jié)中調(diào)用的方法是相同的,此處不再贅述。
- 零基礎(chǔ)學(xué)C++程序設(shè)計(jì)
- arc42 by Example
- Cross-platform Desktop Application Development:Electron,Node,NW.js,and React
- Visual FoxPro 程序設(shè)計(jì)
- 軟件測試項(xiàng)目實(shí)戰(zhàn)之性能測試篇
- Mastering PHP Design Patterns
- 薛定宇教授大講堂(卷Ⅳ):MATLAB最優(yōu)化計(jì)算
- 編譯系統(tǒng)透視:圖解編譯原理
- Mastering Google App Engine
- 零基礎(chǔ)學(xué)Python網(wǎng)絡(luò)爬蟲案例實(shí)戰(zhàn)全流程詳解(高級進(jìn)階篇)
- Learning Material Design
- 寫給程序員的Python教程
- Scala編程(第5版)
- ASP.NET求職寶典
- 青少年學(xué)Python(第2冊)