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