天天看點

Spring AOP 詳解

原理

AOP(Aspect Oriented Programming),也就是面向方面程式設計的技術。AOP基于IoC基礎,是對OOP的有益補充。

  AOP将應用系統分為兩部分,核心業務邏輯(Core business concerns)及橫向的通用邏輯,也就是所謂的方面Crosscutting enterprise concerns,例如,所有大中型應用都要涉及到的持久化管理(Persistent)、事務管理(Transaction Management)、安全管理(Security)、日志管理(Logging)和調試管理(Debugging)等。

  AOP正在成為軟體開發的下一個光環。使用AOP,你可以将處理aspect的代碼注入主程式,通常主程式的主要目的并不在于處理這些aspect。AOP可以防止代碼混亂。

  Spring framework是很有前途的AOP技術。作為一種非侵略性的、輕型的AOP framework,你無需使用預編譯器或其他的元标簽,便可以在Java程式中使用它。這意味着開發團隊裡隻需一人要對付AOP framework,其他人還是像往常一樣程式設計。

  AOP概念

  讓我們從定義一些重要的AOP概念開始。

  — 方面(Aspect):一個關注點的子產品化,這個關注點實作可能另外橫切多個對象。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用Spring的Advisor或攔截器實作。

  — 連接配接點(Joinpoint):程式執行過程中明确的點,如方法的調用或特定的異常被抛出。

  — 通知(Advice):在特定的連接配接點,AOP架構執行的動作。各種類型的通知包括“around”、“before”和“throws”通知。通知類型将在下面讨論。許多AOP架構包括Spring都是以攔截器做通知模型,維護一個“圍繞”連接配接點的攔截器鍊。

  — 切入點(Pointcut):指定一個通知将被引發的一系列連接配接點的集合。AOP架構必須允許開發者指定切入點,例如,使用正規表達式。

  — 引入(Introduction):添加方法或字段到被通知的類。Spring允許引入新的接口到任何被通知的對象。例如,你可以使用一個引入使任何對象實作IsModified接口,來簡化緩存。

  — 目标對象(Target Object):包含連接配接點的對象,也被稱作被通知或被代理對象。

  — AOP代理(AOP Proxy):AOP架構建立的對象,包含通知。在Spring中,AOP代理可以是JDK動态代理或CGLIB代理。

  — 編織(Weaving):組裝方面來建立一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運作時完成。Spring和其他純Java AOP架構一樣,在運作時完成織入。

  各種通知類型包括:

  —  Around通知:包圍一個連接配接點的通知,如方法調用。這是最強大的通知。Aroud通知在方法調用前後完成自定義的行為,它們負責選擇繼續執行連接配接點或通過傳回它們自己的傳回值或抛出異常來短路執行。

  —  Before通知:在一個連接配接點之前執行的通知,但這個通知不能阻止連接配接點前的執行(除非它抛出一個異常)。

  —  Throws通知:在方法抛出異常時執行的通知。Spring提供強制類型的Throws通知,是以你可以書寫代碼捕獲感興趣的異常(和它的子類),不需要從Throwable或Exception強制類型轉換。

  —  After returning通知:在連接配接點正常完成後執行的通知,例如,一個方法正常傳回,沒有抛出異常。

  Around通知是最通用的通知類型。大部分基于攔截的AOP架構(如Nanning和Jboss 4)隻提供Around通知。

  如同AspectJ,Spring提供所有類型的通知,我們推薦你使用最為合适的通知類型來實作需要的行為。例如,如果隻是需要用一個方法的傳回值來更新緩存,你最好實作一個after returning通知,而不是around通知,雖然around通知也能完成同樣的事情。使用最合适的通知類型使程式設計模型變得簡單,并能減少潛在錯誤。例如,你不需要調用在around通知中所需使用的MethodInvocation的proceed()方法,是以就調用失敗。

  切入點的概念是AOP的關鍵,它使AOP差別于其他使用攔截的技術。切入點使通知獨立于OO的層次標明目标。例如,提供聲明式事務管理的around通知可以被應用到跨越多個對象的一組方法上。 是以切入點構成了AOP的結構要素。

首先是大家所熟悉的配置性AOP

附一個AOP的例子:

切面類TestAspect

Java代碼:

ApplicationContext:

<aop:config> <aop:aspect id="TestAspect" ref="aspectBean"> <!--配置com.spring.service包下所有類或接口的所有方法--> <aop:pointcut id="businessService" expression="execution(* com.spring.service.*.*(..))" /> <aop:before pointcut-ref="businessService" method="doBefore"/> <aop:after pointcut-ref="businessService" method="doAfter"/> <aop:around pointcut-ref="businessService" method="doAround"/> <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/> </aop:aspect> </aop:config> <bean id="aspectBean" class="com.spring.aop.TestAspect" /> <bean id="aService" class="com.spring.service.AServiceImpl"></bean> <bean id="bService" class="com.spring.service.BServiceImpl"></bean></beans>

 測試類AOPTest

Java代碼 

切入點表達式

通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:

modifiers-pattern:方法的操作權限

ret-type-pattern:傳回值

declaring-type-pattern:方法所在的包

name-pattern:方法名

parm-pattern:參數名

throws-pattern:異常

其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,傳回值為任意類型;方法名任意;參數不作限制的所有方法。

通知參數

可以通過args來綁定參數,這樣就可以在通知(Advice)中通路具體參數了。例如,<aop:aspect>配置如下