原文連結位址:http://tantanit.com/springinaction4-du-shu-bi-ji-zhi-shi-yong-biao-qian-chuang-jian-aop/
在之前的讀書筆記Spring in Acton 4讀書筆記之AOP原理及Spring對AOP的支援中,講到Spring對AOP的支援包含四方面:
- Spring基于代理的經典的AOP
- 使用XML配置将純POJO轉化為aspect
- 使用Spring的@Aspect标簽,建立aspect
- Spring不建立aspect,隻注入(引用)AspectJ架構的aspect
Spring in Action(Spring實戰)的第四章第三節(4.3 Creating annotated aspects)講述了其中第三種,即如何使用标簽建立aspect。本文講解其中的前面兩小節:定義aspect以及建立around advice。
AspectJ 5引進的主要特性是使用标簽建立aspect。AspectJ 5的缺點是需要學習擴充的java語言。但是AspectJ面向标簽的程式設計模式使得将一個類轉換成aspect變得很容易,隻需要在類中加一些标簽。
定義一個aspect
以下是使用标簽建立AOP的例子,這個例子在之前的文章中有提到過,觀衆在演出開始前後,以及出問題時,會自動做出一些反應:
package concert;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class Audience {
@Before("execution(** concert.Performance.perform(..))")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}
@Before("execution(** concert.Performance.perform(..))")
public void takeSeats() {
System.out.println("Taking seats");
}
@AfterReturning("execution(** concert.Performance.perform(..))")
public void applause() {
 System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("execution(** concert.Performance.perform(..))")
public void demandRefund() {
System.out.println("Demanding a refund");
}
}
Audience類上加上@Aspect,用來表示Audience是一個aspect,而Audience被标注的方法定義了具體的行為。在表演開始前,觀衆需要就座(takeSeats()),将手機靜音(silenceCellPhones())。
表演結束後,觀衆需要鼓掌(applause()),如果表演過程中出現了異常,觀衆會要求退票(demandRefund())。AspectJ提供了五個标簽來定義advice:
- @After,在方法正常執行結束,或者出現異常的時候,執行aspect。
- @AfterReturning,在方法正常執行結束後,執行aspect。
- @AfterThrowing,在方法抛出異常的時候,執行aspect。
- @Around,在方法執行過程中,執行aspect。
- @Before,在方法執行之前,執行aspect。
上面的例子中,所有标簽的值都是一個pointcut表達式,而且在這個例子裡,正好是一樣的(因為是作用在同一個方法上)。實際上,可以将這個pointcut定義好,然後進行引用,這樣可以避免重複編寫pointcut。
@Aspect
public class Audience {
@Pointcut("execution(** concert.Performance.perform(..))")
public void performance() {}
 @Before("performance()")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}
@Before("performance()")
public void takeSeats() {
System.out.println("Taking seats");
}
@AfterReturning("performance()")
public void applause() {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Demanding a refund");
}
}
上面的代碼,使用@Pointcut标簽對performance()方法進行标注,這樣,就可以直接使用performance()來代替pointcut表達式了。performance()隻是一個标記,是以方法體可以也必須是空的。
如果隻是定義了上面Audience這個aspect,那麼其實什麼也做不了。必須有一個配置檔案,指出它是一個aspect,并且解析這些标簽,然後建立代理,最終将Audience轉化為一個aspect。
如果是使用JavaConfig,可以在配置檔案類加上@EnableAspectJAutoProxy标簽,以實作自動代理。以下是示例:
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
@Bean
public Audience audience() {
return new Audience();
}
}
Spring使用AspectJ進行自動代理,僅僅是使用@AspectJ的标簽作為指引,而底層仍然是Spring自己的基于代理的aspect。是以,盡管使用了@AspectJ标簽,Spring的AOP仍然隻能作用在方法級别。如果要使用AspectJ的全部功能,就必須在運作時注入AspectJ,而不使用Spring來建立基于代理的aspect。
建立一個around advice
前面講解了before和after的用法,由于around的用法有些不同,也更有用,是以這裡單講。先看看下面的例子:
@Aspect
public class Audience {
@Pointcut("execution(** concert.Performance.perform(..))")
public void performance() {}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones");
System.out.println("Taking seats");
jp.proceed();
System.out.println("CLAP CLAP CLAP!!!");
} catch (Throwable e) {
System.out.println("Demanding a refund");
}
}
}
使用@Around标簽标注了watchPerformance方法,監聽performance()代表的joinpoint。此時,watchPerformance的參數ProceedingJoinPoint就是指這個joinpoint。可以看到,在jp.proceed()的前後各有一些操作,甚至在抛出異常時,也有一些處理。是以,這個方法同時實作@Before、@AfterReturning和@AfterThrowing等标簽的功能,更加靈活。
需要注意的是,必須執行joinpoint的proceed()方法,否則,會導緻被監聽的方法沒有執行。
我計劃完成50到100篇關于Spring的文章,這是第十一篇,歡迎訂閱tantanit.com,第一時間擷取文章更新,本文連結位址:http://tantanit.com/springinaction4-du-shu-bi-ji-zhi-shi-yong-biao-qian-chuang-jian-aop/
歡迎掃描下方二維碼關注微信公衆号【談談IT】,第一時間擷取最新文章。
