Spring AOP 筆記二(環(huán)繞通知,切面中獲取參數(shù))

此文是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
  • 這樣大概就明白了。

本篇就結束了,有問題的同學評論告訴我,這個結束好生硬。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容