Aop為什么會出現(xiàn)呢?本質(zhì)上來說,是為了實現(xiàn)單一職責(zé)原則的思想,我們在做一個save操作的時候,往往需要先開啟事務(wù),如果成功提交事務(wù),失敗了回滾事務(wù),最后還得關(guān)閉事務(wù)。最后說的這些事情其實在所有的業(yè)務(wù)邏輯中都是需要的,所以說都是些重復(fù)的操作,顯然這樣很不"程序猿"。于是為了對業(yè)務(wù)進行增強,加入一些必要的附屬操作,AOP就這么誕生出來了
其實有可能你會發(fā)現(xiàn),AOP好像和代理模式非常的相似,沒錯,AOP就是用代理模式進行實現(xiàn)的。
代理模式可以分為兩種
-
靜態(tài)代理
在編譯期間就已經(jīng)存在一個代理類
-
動態(tài)代理
不存在代理類的字節(jié)碼文件,是通過反射動態(tài)生成的,是在運行期間,才確定關(guān)系的。
1.Java動態(tài)代理(真實對象必須存在接口)
- 原理:動態(tài)代理其實本質(zhì)上也是靜態(tài)代理的實現(xiàn)方法,只不過代理類不是由我們提供的,而是通過程序生成一個字節(jié)碼文件,然后加載進虛擬機中。是通過實現(xiàn)接口的方式
- 使用的是reflect包下的Proxy和InvocationHandler 接口進行代理的
- 動態(tài)代理的最小單位是類,也就是說會為該類的所有public方法進行增強。(但是可以通過判斷方法的一些特性,決定是否放行)
2.CGlib動態(tài)代理
- 原理: 通過繼承了實現(xiàn)類的方式,對方法進行增強
- 被代理的類必須得是非final的
性能方面: Javassit > CGlib > JDK
Tips: AOP 使用了一種攔截器(Interceptor)的思想,相對于Filter只能應(yīng)用于web領(lǐng)域,interceptor可以應(yīng)用于各個領(lǐng)域,應(yīng)用范圍更過
AOP之關(guān)鍵字
- JoinPoint 連接點,連接的是需要被增強的方法,強調(diào)的是去哪里做增強
- PointCut 切入點, 是JoinPoint的一個集合,強調(diào)的是取哪些地方做增強。
- Advice 增強,在方法執(zhí)行的某一個時機,應(yīng)該如何做增強。增強的類型共分為5種:
- 前置增強
- 后置增強
- 異常增強
- 最終增強
- 環(huán)繞增強
- Target 目標(biāo)對象
- Aspect 切面 = JoinPoint + Advice
- weaving 植入,把advise加到target上,然后創(chuàng)建代理過程的對象
PointCut語法
參考spring官網(wǎng)
AspectJ與動態(tài)代理
他倆最大的差別就在于動態(tài)代理是在運行的時候生成相應(yīng)的class文件,而AspectJ則不是,它是通過編譯成字節(jié)碼文件的時候就被織入了字節(jié)碼文件中,所以AspectJ也可以切私有方法,而動態(tài)代理卻做不到。
AOP的配置使用
xml方式
在xml文件中加入了aop的命名空間后,寫入以下代碼:
<aop:config>
<aop:aspect ref="使用哪個類做增強">
<aop:pointcut id="pointcutA" expression="pointcut的execution表達式"/> <!--對哪些方法做增強-->
<aop:before method="aop:aspect標(biāo)簽中ref屬性的某個方法" pointcut-ref="pointcutA"/><!--調(diào)用前增強-->
<aop:after-returning method="aop:aspect標(biāo)簽中ref屬性的某個方法" pointcut-ref="pointcutA" /><!--對返回后增強-->
<aop:after-throwing method="aop:aspect標(biāo)簽中ref屬性的某個方法" pointcut-ref="pointcutA" throwing =""/> <!--出現(xiàn)異常后增強-->
<aop:after-throwing method="aop:aspect標(biāo)簽中ref屬性的某個方法" pointcut-ref="pointcutA" /> <!--最終增強,無論有沒有異常,適用于釋放資源-->
<aop:around />
</aop:aspect>
</aop:config>
注解方式
與上面的xml方式,十分類似也可以一一對應(yīng)上。
- 首先需要在切面類上面使用@aspect
- 如果是前置的增強使用@Before
- 如果是返回后置的使用@AfterReturning
- 如果是異常后置的使用@AfterThrowing
- 如果是最后的返回,使用@After
- 如果是全方位的進行定制化,使用@Around
對應(yīng)的這些注解擁有的屬性,也是和xml中擁有的屬性幾乎相同。
Tips:經(jīng)過我的實驗證明@After會在@AfterReturning 和@AfterThrowing之前執(zhí)行的
那么問題來了,我們現(xiàn)在想要獲取被切的對象的相關(guān)信息應(yīng)該如何做呢?
1.如果是異常增強:我們可以通過配置配置throwing參數(shù),拿到被拋出的異常的對象。
2.除了異常增強和環(huán)繞增強之外,可以通過JoinPoint作為第一個參數(shù)獲取到相關(guān)的信息,包括被切的對象,方法參數(shù)等等
3.如果是應(yīng)用于環(huán)繞增強那么,使用ProcessingJoinPoint,它其實是JoinPoint的一個子類,不僅可以獲取相關(guān)的信息,還可以調(diào)用真實被環(huán)繞的方法。