Spring AOP原理總結
動態代理
- JDK動態代理:只能為介面創建動態代理實例,而不能針對類 。
- CGLib(Code Generation Library)動態代理:可以為任何類創建織入橫切邏輯代理對象,主要是對指定的類生成一個子類,覆蓋其中的方法,因為是繼承,所以該類或方法最好不要聲明成final。
原理對比:
- JDK動態代理:JDK動態代理技術。通過需要代理的目標類的getClass().getInterfaces()方法獲取到介面信息(這裡實際上是使用了Java反射技術。getClass()和getInterfaces()函數都在Class類中,Class對象描述的是一個正在運行期間的Java對象的類和介面信息),通過讀取這些代理介面信息生成一個實現了代理介面的動態代理Class(動態生成代理類的位元組碼),然後通過反射機制獲得動態代理類的構造函數,並利用該構造函數生成該Class的實例對象(InvokeHandler作為構造函數的入參傳遞進去),在調用具體方法前調用InvokeHandler來處理。
- CGLib動態代理:位元組碼技術。利用asm開源包,把代理對象類的class文件載入進來,通過修改其位元組碼生成子類來處理。採用非常底層的位元組碼技術,為一個類創建子類,並在子類中採用方法攔截的技術攔截所有父類方法的調用,並順勢織入橫切邏輯。
Spring AOP
- Pointcut(切點):指定在哪些類的哪些方法上織入橫切邏輯
- Advice(增強):描述橫切邏輯和方法的具體織入點(方法前、方法後、方法的兩端等)
- Advisor(切面):將Pointcut和Advice兩者組裝起來。有了Advisor的信息,Spring就可以利用JDK或CGLib的動態代理技術採用統一的方式為目標Bean創建織入切面的代理對象了
AOP的動態代理:
我們所說的Spring AOP,它包括基於XML配置的AOP和基於@AspcetJ註解的AOP,這兩種方法雖然在配置切面時的表現方式不同,但底層都是使用動態代理技術(JDK代理或CGLib代理)。
Spring可以繼承AspcetJ,但AspcetJ本身並不屬於Spring AOP的範疇:
- AspectJ:
AspectJ 在編譯時「自動」編譯得到了一個新類,這個新類增強了原有的 Hello.java 類的功能,因此 AspectJ 通常被稱為編譯時增強的 AOP 框架
與 AspectJ 相對的還有另外一種 AOP 框架,它們不需要在編譯時對目標類進行增強,而是運行時生成目標類的代理類,該代理類要麼與目標類實現相同的介面,要麼是目標類的子類——總之,代理類的實例可作為目標類的實例來使用。一般來說,編譯時增強的 AOP 框架在性能上更有優勢——因為運行時動態增強的 AOP 框架需要每次運行時都進行動態增強。
- Spring AOP:
與 AspectJ 相同的是,Spring AOP 同樣需要對目標類進行增強,也就是生成新的 AOP 代理類;與 AspectJ 不同的是,Spring AOP 無需使用任何特殊命令對 Java 源代碼進行編譯,它採用運行時動態地、在內存中臨時生成「代理類」的方式來生成 AOP 代理。
Spring 允許使用 AspectJ Annotation 用於定義方面(Aspect)、切入點(Pointcut)和增強處理(Advice),Spring 框架則可識別並根據這些 Annotation 來生成 AOP 代理。Spring 只是使用了和 AspectJ 5 一樣的註解,但並沒有使用 AspectJ 的編譯器或者織入器(Weaver),底層依然使用的是 Spring AOP,依然是在運行時動態生成 AOP 代理,並不依賴於 AspectJ 的編譯器或者織入器。簡單地說,Spring 依然採用運行時生成動態代理的方式來增強目標對象,所以它不需要增加額外的編譯,也不需要 AspectJ 的織入器支持;而 AspectJ 在採用編譯時增強,所以 AspectJ 需要使用自己的編譯器來編譯 Java 文件,還需要織入器。@AspectJ的使用:
如果不打算使用 Spring 的 XML Schema 配置方式,則應該在 Spring 配置文件中增加如下片段來啟用 @AspectJ 支持:
<!-- 啟動 @AspectJ 支持 -->n<bean class="org.springframework.aop.aspectj.annotation. n AnnotationAwareAspectJAutoProxyCreator"/>n
上面配置文件中的 AnnotationAwareAspectJAutoProxyCreator 是一個 Bean 後處理器(BeanPostProcessor),該 Bean 後處理器將會為容器中 Bean 生成 AOP 動態代理。
使用 @Aspect 標註一個 Java 類,該 Java 類將會作為方面 Bean(也是可以被Spring容器管理的Bean),如下面代碼片段所示:
// 使用 @Aspect 定義一個方面類n@Aspect npublic class LogAspect n{ n // 定義該類的其他內容n ... n}n
當我們使用 @Aspect 來修飾一個 Java 類之後,Spring 將不會把該 Bean 當成組件 Bean 處理,因此負責自動增強的後處理 Bean 將會略過該 Bean,不會對該 Bean 進行任何增強處理。開發時無須擔心使用 @Aspect 定義的方面類被增強處理,當 Spring 容器檢測到某個 Bean 類使用了 @Aspect 標註之後,Spring 容器不會對該 Bean 類進行增強。
CGLib代理與JDK動態代理:
1、如果目標對象實現了介面,默認情況下會採用JDK的動態代理實現AOP
2、如果目標對象實現了介面,可以強制使用CGLIB實現AOP
3、如果目標對象沒有實現了介面,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換如何強制使用CGLIB實現AOP? :
1、添加CGLIB庫
2、在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>AOP自動代理原理:
Spring提供了自動代理機制,讓容器為我們自動生成代理。在內部,Spring使用BeanPostProcessor自動地完成這項工作。
這些基於BeanPostProcessor的自動代理創建器的實現類,將根據一些規則自動在容器實例化Bean時為匹配的Bean生成代理實例:
- 基於Bean配置名規則的自動代理創建器:允許為一組特定配置名的Bean自動創建代理實例的代理創建器,實現類為BeanNameAutoProxyCreator;
- 基於Advisor匹配機制的自動代理創建器:它會對容器中所有的Advisor進行掃描,自動將這些切面應用到匹配的Bean中(即為目標Bean創建代理實例),實現類為DefaultAdvisorAutoProxyCreator;
- 基於Bean中AspjectJ註解標籤的自動代理創建器:為包含AspectJ註解的Bean自動創建代理實例,它的實現類是AnnotationAwareAspectJAutoProxyCreator,該類是Spring 2.0的新增類。
註:
通過Spring IOC原理可知:BeanPostProcessor 是容器級生命周期介面方法 ,該後處理器介面不由 Bean 本身實現,它們獨立於 Bean,實現類以容器附加裝置的形式註冊到 Spring 容器中並通過介面反射為 Spring 容器預先識別。當Spring 容器創建任何 Bean 的時候,這些後處理器都會發生作用,所以這些後處理器的影響是全局性的。當然,用戶可以通過合理地編寫後處理器,讓其僅對感興趣Bean 進行加工處理。
參考來源:
《Spring 3.x企業應用開發實戰》https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html http://blog.csdn.net/u013126379/article/details/52121096
(本文首發於公眾號:EnjoyMoving)
推薦閱讀:
※如何理解 ssh 三大框架?
※怎麼閱讀Spring源碼?
※Ribbon源碼分析系列(一)
※周末薦書 | 《Spring實戰》(第4版)