此文是Sping in Action 第4版 英文原版切面部分的讀書筆記,僅限交流使用,有不足之處,一定聽取修改。
系列目錄:
Spring AOP 筆記一(基礎概念,一個簡單切面)
Spring AOP 筆記二(環(huán)繞通知,切面中獲取參數(shù))
Spring AOP 筆記三(切面注解引入新的方法)
接上,上一篇的示例切面中我們重復寫了三次代碼。維護起來比較困難,可以在切面配置類中聲明一個切點方法,相當于聲明一個切面中的全局切點。
1. 切點方法 代碼如下:
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;
import org.aspectj.lang.annotation.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()方法。如何調(diào)用就在代碼中。
1.1 使用java方式進行切面配置
上一篇文章已經(jīng)代碼貼出,這里就回憶一下注解的意思:
- @Configration:聲明當前類是一個配置類。
- @EnableAspectJAutoProxy 激活Spring對AspectJ的自動代理功能的支持。
- @ComponentScan() 自動掃描指定包名下所有使用@Service,@Component,@Repository,@Controller的類,如果沒有指定包名,會默認掃描類所在的包。
2 創(chuàng)建環(huán)繞通知(around advice)
環(huán)繞通知相當于after和before的合體。
下面重寫Audience切面:
package concert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Audience {
@Pointcut("execution(* com.memgxiang.concert.Performance.perform(..))")
public void performance() {}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("手機靜音");
System.out.println("得到座位");
jp.proceed();
System.out.println("鼓掌!!!");
} catch (Throwable e) {
System.out.println("這演的啥??!退票");
}
}
}
這里的 @Around注解就聲明了watchPerformance()方法成為了一個環(huán)繞通知切點。他的運行結果與上例使用@Before,@After等等的效果相同??梢园l(fā)現(xiàn)watchPerformance()方法中給出了一個PreceedingJoinPoint類型的參數(shù),這個是必須的,因為你需要告訴該切點中的任務相對與你的工作所處的位置。使用ProceedingJoinPoint’s proceed()方法將你的任務放在你想要的位置。
3. 在通知中獲取參數(shù)。
在上面的@Around例子中,watchPerformance()只有一個用于說明被切面的方法如何切入的參數(shù),而且這里的performance()方法中也沒有任何參數(shù),當被切入的方法沒有參數(shù)的時候,可以使用上例中的那些簡單方式。
不過當被切入(或者說被通知)的方法存在參數(shù)時,我們想要在通知中獲取到該參數(shù),就使用下面的方式。
我對原書中的示例做了簡化。
首先定義一個類,有一個帶int類型參數(shù)的方法 playTrack() ,該方法就是將要被切入的方法。
/**
* 緊湊圓盤。
* Created by Henvealf on 2016/8/28.
*/
public class CompactDisc {
public void playTrack(int trackNumber){
System.out.println("trackNumber =" + trackNumber);
}
}
隨后就是定義一個切面:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
*
* Created by Henvealf on 2016/8/28.
*/
@Aspect
public class TrackCounter {
@Pointcut("execution(* com.mengxiang.concert.CompactDisc.playTrack(int)) && args(trackNum)")
public void trackPlayed(int trackNum) {}
@Before("trackPlayed(trac)")
public void countTrack(int trac) {
System.out.println("我在切面中,我會在playTrack()運行前被調(diào)用" + trac);
}
}
- 測試類在這里就不列出了。
這里我們用 @PointCut 注解了一個帶有int參數(shù)的方法 trackPlayed() ,將其定義為一個切點。在 @PointCut 中我們?nèi)テヅ?CompactDisc 類中的 playTrack() 方法,并且定制了其參數(shù)類型為int,這里當這樣限制了被切入方法的類型,就必須同時使用 args() 標識符來指明變量的名字,且名字要與定義切點的方法(trackPlayed)中的參數(shù)名字相比配。
之后在 countTrack()上使用@Before()來定義一個通知。里面直接使用了上面定義的切點方法,并且切點方法中加入了一個代表參數(shù)名的標識trackNumber,此標識與countTrack中的參數(shù)名必須相匹配。
從上面的例子可以發(fā)現(xiàn),args()中的參數(shù)名標識不需要與被通知的方法的參數(shù)名一致,它只是為了在切點內(nèi)部,互相區(qū)分參數(shù)列表中的各個參數(shù)。
為了說明我們將被切入的類CompactDisc中的 playTrack 方法修改成具有兩個參數(shù):
public void playTrack(int trackNumber,int number){
System.out.println("trackNumber =" + trackNumber);
System.out.println("number =" + number);
}
隨之我們修改切點為:
@Pointcut("execution(* com.mengxiang.concert.CompactDisc.playTrack(int,int)) && args(trackNumb,numb))")
public void trackPlayed(int trackNumb,int numb) {}
@Before("trackPlayed(trackNumber,number)")
public void countTrack(int trackNumber, int number) {
System.out.println("我在切面中,我會在playTrack()運行前被調(diào)用" + trackNumber);
System.out.println("我在切面中,我會在playTrack()運行前被調(diào)用" + number);
}
注意@Pointcut中的 playTrack(int,int) 與 args(trackNumb,numb)。
當我們調(diào)用playTrack(20,40)后,發(fā)現(xiàn)輸出為:
我在切面中,我會在playTrack()運行前被調(diào)用20
我在切面中,我會在playTrack()運行前被調(diào)用40
trackNumber =22
現(xiàn)在我們將@Pointcut注解的trackPlayed()中的兩個參數(shù)調(diào)換。即:
- trackPlayed(int numb,int trackNumb){..}
會發(fā)現(xiàn)輸出結果出現(xiàn)了變化:
我在切面中,我會在playTrack()運行前被調(diào)用40
我在切面中,我會在playTrack()運行前被調(diào)用20
trackNumber =22
number =40
- 這樣大概就明白了。
本篇就結束了,有問題的同學評論告訴我,這個結束好生硬。