第三章 實現AOP
AOP:面向方面程式設計,AOP能夠使您將所有模組共有的特性與應用程式的主要業務邏輯隔離開
一、AOP介紹
橫切關注點:在Web應用程式中,有一些服務(如登入、安全和事務管理)不是應用程式的主要業務邏輯的一部分。但是,這些服務對於Web應用程式是必不可少的,需要在應用程式的每個模組中實現。因此,這些服務被認為是橫切關注點,因為它們要橫切應用程式的多個模組。
次要問題,因為他們不實現應用程式的核心功能。
1.定義
將程式碼分解成不同的模組,稱為方面,切面支援對關注點進行模組化,比如跨多個型別和物件的事物管理、日誌記錄和安全性。
AOP的解決方案:不必從方法中呼叫所有方法,而是在類的方法中定義額外的關注點,比如維護日誌等。
2.AOP中的概念與術語
方面:方面是跨多個類的企業應用程式關注點的類。Aspect可以是通過spring XML檔案配置的普通類,也可以使用spring AspectJ整合。使用@Aspect將類定義為Aspect
連線點:連線點是應用程式中的一個特定點,比如方法的執行、異常處理等。在spring aop中,連線點總是方法的執行。
通知:通知是針對某個特定的連線點而採取的行動,相當於是在應用程式中到達某個具有匹配切入點的某個連線時候執行的方法。
分類標準:通知的執行方法,spring AOP中有5類通知
前通知:在連線點之前執行的通知,但是不能組織執行流繼續到連線點(除非丟擲異常)
返回後通知:通知將在連線點正常完成後執行
丟擲後通知:如果方法通過丟擲異常而退出,則執行通知
後通知:無論連線點以何種方式退出,都要執行通知
圍繞通知:圍繞連線點的通知,可以在方法呼叫之前和呼叫智慧執行自定義行為。它也負責選擇是繼續到連線點還是通過返回自身的返回值或者丟擲一場來簡化被建議的方法的執行。
切入點:切入點是與連線點匹配的表示式,以確定是否需要執行通知。(最終應用通知的所選連線點稱為切入點)
目標物件Target Object:通知適用的物件。意思是執行時建立一個子類,其中覆蓋目標方法,並更具其配置包含通知
AOP代理:Spring AOP實現通過適用JDK動態代理建立帶有目標類和通知呼叫的代理類
織入:它是將方面與其他物件連結以建立已通知代理物件的過程
二、實現AOP
AOP的實現有三個主要的AOP框架提供
spring AOP Aspect J JBoss AOP
三種使用spring AOP的方法
通過使用spring API
使用Aspect J 註釋風格
通過spring XML配置樣式與spring一起工作
面向方面程式設計與spring一起工作
spring使用基於代理的機制,它建立了一個代理物件,該代理物件包裹原始物件。並講接收與方法呼叫相關的通知。代理物件可以通過代理工廠bean建立,也可以通過XML檔案中的自動代理配置建立並在執行完成時銷燬。
1.spring Aspect J風格的AOP
通過使用Aspect J 提供的用於切入點解析和匹配的庫。AOP執行時候仍然是純粹的spring AOP,而且不依賴於Aspect J 編譯器或者weaver
配置@Aspect J 支援:XML配置或者Java配置
note:兩種都要有aspectjweaver.jar庫位於應用程式的類路徑中。
使用Java類配置啟用
新增@EnableAspectJAutoproxy註釋
@Configuration
@EnableAspectJAutoProxy
public class AppConfig{
}
spring AspectJ AOP提供了許多註釋
@Aspect將類宣告為aspect
@Pointcut宣告切入點表示式
用於建立通知的註釋
@Before 宣告前通知 在呼叫實際方法之前應用它
@After 聲明後通知。在呼叫實際方法之後和返回結果之前應用。
@AfterReturning 宣告返回後通知。 在呼叫實際方法和返回結果之前應用,但是可以在通知中獲取結果值。
@Around 聲明瞭圍繞通知。 在呼叫實際方法之前和之後應用
@AfterTrowing 聲明瞭丟擲後通知。 如果實際方法丟擲應用,則呼叫該方法。
宣告方面
啟用@Aspect J 支援後,在應用程式上下文中定義的帶有@Aspect J 的方面類的任何bean都會唄spring 自動檢測並用於配置Spring AOP。
應用程式上下文中的 一個常規bean定義,指向一個帶有@Aspect註釋的bean類
宣告切入點
spring AOP只執行spring bean的方法執行連線點,將切入點看作匹配spring bean上方法的執行。
切入點宣告:簽名(由名稱和任何引數組成) 切入點表示式(準確確定我們意向的方法執行)
分別由一個常規方法定義提供 和 @Pointcut註釋表示
NOTE:作為切入點簽名的方法必須有一個void返回型別
Example:
區分切入點簽名和切入點表示式
@Pointcut ("excution(*transfer(...))") //the pointcut expression
private void anyOldtransfer(){ } //the pointcut signature
常見切入點的示例
任何公共方法的執行:
excution(public * *(...))
任何以"Xx"開頭的方法的執行
excution(* xx*(...))
AccountService介面定義的任何方法的執行
excution(* com.xyz.service.AcountService.*(...))
任何服務包中定義的方法執行
excution(* com.xyz.service.*.*(..))
任何服務包或者子包定義的方法的執行
excution(* com.xyz. service.*. *(..))
服務包中的任何連線點
within(com.xyz.service.*)
宣告通知
通知於切入點表示式關聯,並在切入點匹配的方法執行之前、之後或者周圍執行。切入點表示式可是對命名切入點的簡單引用,也可以是在恰當位置宣告的切入點表示式
import org.aspectj.lang.annotation.Aspect;
前通知
@Before("com.niit.myapp.SystemArchitecture.dataAccessOperation()")
返回後通知
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
NOTE:返回標記屬性中使用的名稱必須與通知方法中的引數相對應。return子句害將匹配現狀為只匹配哪些返回指定型別值的方法的執行。
丟擲後通知
@AfterThrowing("com.niit.myapp.SystemArchitecture.dataAccessOperation()")
使用丟擲屬性嚴格匹配,將丟擲的異常繫結到一個通知引數
丟擲屬性中使用的名稱必須與通知方法中的引數名稱對應。當方法執行丟擲異常而退出時,異常將作為相應的引數值傳遞給通知方法。
後通知
後通知執行時,匹配的方法執行退出。後通知必須住呢比阿紅處理正常和異常返回條件,一般用於釋放資源。
@After("com.niit.myapp.SystemArchitecture.dataAccessOperation()")
圍繞通知
它在方法執行之前和之後執行工作,並確定何時、如何以及是否該方法實際執行。如果需要以執行緒安全的方式在方法執行之前和之後共享狀態,則使用它。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.ProceedingJoinPoint;
NOTE:最弱方式,即如果前通知可以做,就不要使用圍繞通知。
@Around("com.niit.myapp.SystemArchitecture.dataAccessOperation()")
使用XML配置啟用@AspectJ支援
<aop:aspectj-autoproxy/>
2.基於spring 模式的AOP(XML配置)
宣告方面
使用模式支援,方面只是在spring應用程式上下文中定義為bean的常規Java物件。狀態和行為在物件的欄位和方法中捕獲,切入點和通知信心在XML中捕獲。
<aop:config>
<aop:aspect id="myAspect" ref="aBean"> //宣告方面。後臺Bean,使用ref屬性引用
...
</aop:aspect>
</aop:config>
宣告切入點
切入點是Spring AOP中的一種表示式語言,表示服務層中任何業務執行的切入點可以定義如下:
<aop:config>
<aop:pointcut id="bussinessService" expression="execution(* com.niit.myapp.service.*.*(..))"/>
</aop:config>
切入點表示式
應用於所有公共方法
<aop:pointcut expression=("execution(public * *(..))")>
應用於所有操作類的公共方法
<aop:pointcut expression=("execution(public Operation. *(..))")>
應用於操作類的所有方法
<aop:pointcut expression=("execution(Operation.*(..))")>
應用於所有員工類的公共setter方法
<aop:pointcut expression="execution(public Employee.set . *(..))")>
應用於所有操作類返回int 值的方法
<aop:pointcut expression="execution(int Operation.*(..))")>
宣告通知
前通知
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut-ref="dataAccessOperation" method="doAccessCheck"/>
</aop:aspect >
返回後通知
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation" method="doAccessCheck"
returning="retval"/>
</aop:aspect >
可以在通知主體中獲得返回值,使用返回值標記屬性來指定應該傳遞返回值的引數的名稱。
新增一個returning 屬性
doAccessCheck方法必須指明一個返回值的引數的名稱,簽名的方式可以是
public void doAccessCheck(Object reVal){...}
丟擲後通知
當匹配的方法執行通過丟擲異常結束時執行通知。
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing ="dataAccessEx"
method="doAccessRecoveryActions"/>
</aop:aspect >
使用丟擲標記屬性指定異常一個傳遞到的引數名稱
新增一個throwing屬性,對應的方法必須宣告一個對應的引數(同上)
後通知
在匹配的方法執行退出時執行
<aop:aspect id=afterExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation" method="doRelease"/>
</aop:aspect >
圍繞通知
(表述和用註釋配置相同)
圍繞通知是使用aop:Around元素宣告的,通知方法的第一個引數必須是ProceedingJoinPoint型別,在通知的主體中,對ProceedingJoinPoint呼叫proceed()將執行底層方法。
<aop:aspect id=afterExample" ref="aBean">
<aop:around
pointcut-ref="dataAccessOperation" method="doBasicProfiling"/>
</aop:aspect >
3.使用spring API的AOP
步驟:
建立通知——>定義切入點——>建立代理
建立代理
通知是方面在特定連線點上採取的操作。它是通過建立一個函式,來表示應用程式的輔助邏輯實現的。
一個應用程式可以有一個或者多個通知。
spring API aop中實現4種類型的通知
前通知在實際方法呼叫之前執行
後通知在實際的方法呼叫之後執行,如果方法返回值,則在返回值之後執行。
圍繞通知在實際方法呼叫之前和之後執行。
丟擲後通知,如果實際方法丟擲異常,則執行該方法。
前通知:
它沒有中斷程式執行的能力,除非丟擲異常。
匯入org.springfrawork.aop.MethodBeforeAdvice 介面
在介面中定義before方法
public void before{
Method method,Object [] args,Object target
}throws Throwable
method:第一個引數,表示通知用到的方法
args:是物件型別的陣列,該陣列包含呼叫方法時傳遞給方法的引數
target:表示方法呼叫的目標
返回後:
這個通知再連線帶你正常完成後執行
匯入org.springframework.aop.AfterReturningAdvice介面
public void afterReturning(Object returnValue, Method method,Object[] arg,
Oject target)throws Throwable
四個引數
returnValue:儲存從被呼叫的方法返回的值
其餘三個引數同上
returnValue:儲存從被呼叫的方法返回的值
丟擲後
匯入org.springframework.aop.ThrowsAdvice介面
public void afterThrowing (Throwable throwbale)
after-throwing()方法接收可丟擲型別的一個引數,該引數指示應由通知處理的異常。
建立通知,spring提供的配置元素——
aop:config:定義其中所有AOP的配置,它可以包含定義切入點、通知和方面的元素
aop:aspect:用於宣告一個方面,有一個ref標記屬性,該屬性引用spring配置檔案中的定義的相應的方面bean
aop:before:用於定義連線點之前執行的一個方法
aop:after-returning:用於定義連線點之後執行的一個方法
aop:after-throwing:用於定義丟擲時異常執行的一個方法
aop:around:用於定義連線點之前和之後執行的一個方法
定義切入點
定義切入點來說明通知要被應用的位置。
切入點的主要目的是選擇一個通知應用的地方的方法。切入點在spring配置檔案中定義。
要辨別可以應用通知的特定位置或方法,需要指定模式
此模式包含使用正則表示式呼叫的方法的名稱,正則表示式為匹配文字字串
使用正則表示式定義切入點:
org.springframework.aop.support.JdkRegexpMethodPointcut類
pattern屬性,該屬性的value標記屬性用於指定呼叫方法的特定模式
使用萬用字元指定模式,星號*,表示任何類、方法、返回型別或者引數;或者零個或者多個引數的點字元(..)來指定切入點的模式
建立通知器 org.springframework.aop.support.DefaultPonitcutAdvisor,使用其再spring配置檔案中定義一個advisor bean
Example:
建立代理
建立一個呼叫該advisor的地理
代理充當通知、目標物件、連線點、切入點和通知器之間的連結
代理在spring配置檔案中作為bean建立,spring提供了org.springframework.aop.config.ProxyFactoryBean類
傳遞以下引數來建立一個代理
Target:指定您想代理代理的目標物件
interceptorName:代表型別的陣列,字串。指定在spring 配置檔案中advisor bean名稱列表
proxyInterfaces:代表型別的陣列,字串。指定定義應用程式主要邏輯的介面名稱
Example:
mypointCutAdivice