此文是Sping in Action 第4版 英文原版切面部分的讀書筆記能曾,僅限交流使用嫁怀,有不足之處设捐,一定聽取修改。
系列目錄:
Spring AOP 筆記一(基礎(chǔ)概念塘淑,一個(gè)簡單切面)
Spring AOP 筆記二(環(huán)繞通知,切面中獲取參數(shù))
Spring AOP 筆記三(切面注解引入新的方法)
接上蚂斤,上一篇的示例切面中我們重復(fù)寫了三次代碼存捺。維護(hù)起來比較困難,可以在切面配置類中聲明一個(gè)切點(diǎn)方法曙蒸,相當(dāng)于聲明一個(gè)切面中的全局切點(diǎn)捌治。
1. 切點(diǎn)方法 代碼如下:
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ìn)行切面配置
上一篇文章已經(jīng)代碼貼出肖油,這里就回憶一下注解的意思:
- @Configration:聲明當(dāng)前類是一個(gè)配置類。
- @EnableAspectJAutoProxy 激活Spring對(duì)AspectJ的自動(dòng)代理功能的支持臂港。
- @ComponentScan() 自動(dòng)掃描指定包名下所有使用@Service,@Component,@Repository,@Controller的類森枪,如果沒有指定包名,會(huì)默認(rèn)掃描類所在的包审孽。
2 創(chuàng)建環(huán)繞通知(around advice)
環(huán)繞通知相當(dāng)于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("手機(jī)靜音");
System.out.println("得到座位");
jp.proceed();
System.out.println("鼓掌!!!");
} catch (Throwable e) {
System.out.println("這演的啥啊佑力!退票");
}
}
}
這里的 @Around注解就聲明了watchPerformance()方法成為了一個(gè)環(huán)繞通知切點(diǎn)式散。他的運(yùn)行結(jié)果與上例使用@Before,@After等等的效果相同〈虿可以發(fā)現(xiàn)watchPerformance()方法中給出了一個(gè)PreceedingJoinPoint類型的參數(shù)暴拄,這個(gè)是必須的,因?yàn)槟阈枰嬖V該切點(diǎn)中的任務(wù)相對(duì)與你的工作所處的位置编饺。使用ProceedingJoinPoint’s proceed()方法將你的任務(wù)放在你想要的位置乖篷。
3. 在通知中獲取參數(shù)。
在上面的@Around例子中反肋,watchPerformance()只有一個(gè)用于說明被切面的方法如何切入的參數(shù)那伐,而且這里的performance()方法中也沒有任何參數(shù),當(dāng)被切入的方法沒有參數(shù)的時(shí)候石蔗,可以使用上例中的那些簡單方式罕邀。
不過當(dāng)被切入(或者說被通知)的方法存在參數(shù)時(shí),我們想要在通知中獲取到該參數(shù)养距,就使用下面的方式诉探。
我對(duì)原書中的示例做了簡化。
首先定義一個(gè)類棍厌,有一個(gè)帶int類型參數(shù)的方法 playTrack() 肾胯,該方法就是將要被切入的方法竖席。
/**
* 緊湊圓盤。
* Created by Henvealf on 2016/8/28.
*/
public class CompactDisc {
public void playTrack(int trackNumber){
System.out.println("trackNumber =" + trackNumber);
}
}
隨后就是定義一個(gè)切面:
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("我在切面中敬肚,我會(huì)在playTrack()運(yùn)行前被調(diào)用" + trac);
}
}
- 測(cè)試類在這里就不列出了毕荐。
這里我們用 @PointCut 注解了一個(gè)帶有int參數(shù)的方法 trackPlayed() ,將其定義為一個(gè)切點(diǎn)艳馒。在 @PointCut 中我們?nèi)テヅ?CompactDisc 類中的 playTrack() 方法憎亚,并且定制了其參數(shù)類型為int,這里當(dāng)這樣限制了被切入方法的類型弄慰,就必須同時(shí)使用 args() 標(biāo)識(shí)符來指明變量的名字,且名字要與定義切點(diǎn)的方法(trackPlayed)中的參數(shù)名字相比配第美。
之后在 countTrack()上使用@Before()來定義一個(gè)通知。里面直接使用了上面定義的切點(diǎn)方法陆爽,并且切點(diǎn)方法中加入了一個(gè)代表參數(shù)名的標(biāo)識(shí)trackNumber什往,此標(biāo)識(shí)與countTrack中的參數(shù)名必須相匹配。
從上面的例子可以發(fā)現(xiàn)慌闭,args()中的參數(shù)名標(biāo)識(shí)不需要與被通知的方法的參數(shù)名一致别威,它只是為了在切點(diǎn)內(nèi)部,互相區(qū)分參數(shù)列表中的各個(gè)參數(shù)贡必。
為了說明我們將被切入的類CompactDisc中的 playTrack 方法修改成具有兩個(gè)參數(shù):
public void playTrack(int trackNumber,int number){
System.out.println("trackNumber =" + trackNumber);
System.out.println("number =" + number);
}
隨之我們修改切點(diǎn)為:
@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("我在切面中兔港,我會(huì)在playTrack()運(yùn)行前被調(diào)用" + trackNumber);
System.out.println("我在切面中,我會(huì)在playTrack()運(yùn)行前被調(diào)用" + number);
}
注意@Pointcut中的 playTrack(int,int) 與 args(trackNumb,numb)仔拟。
當(dāng)我們調(diào)用playTrack(20,40)后衫樊,發(fā)現(xiàn)輸出為:
我在切面中,我會(huì)在playTrack()運(yùn)行前被調(diào)用20
我在切面中利花,我會(huì)在playTrack()運(yùn)行前被調(diào)用40
trackNumber =22
現(xiàn)在我們將@Pointcut注解的trackPlayed()中的兩個(gè)參數(shù)調(diào)換科侈。即:
- trackPlayed(int numb,int trackNumb){..}
會(huì)發(fā)現(xiàn)輸出結(jié)果出現(xiàn)了變化:
我在切面中,我會(huì)在playTrack()運(yùn)行前被調(diào)用40
我在切面中炒事,我會(huì)在playTrack()運(yùn)行前被調(diào)用20
trackNumber =22
number =40
- 這樣大概就明白了臀栈。
本篇就結(jié)束了,有問題的同學(xué)評(píng)論告訴我挠乳,這個(gè)結(jié)束好生硬权薯。