(筆記)Spring實戰_面向切面的Spring(4)_註解切面

AspectJ面向註解的模型可以非常簡便地通過少量註解把任意類轉變成切面。這種新特性通常稱爲@AspectJ。

package com.springinaction.springidol;


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 AudienceOfAspectJ
{

    @Pointcut("execution(* com.springinaction.springidol.Performer.perform(..))")
    public void performance()
    {

    }

    @Before("performance()")
    public void takeSeats()
    {
        System.out.println("The audience is taking their seats.");
    }

    @Before("performance()")
    public void turnOffCellPhones()
    {
        System.out.println("The audience is turning off their cellphones.");
    }

    @AfterReturning("performance()")
    public void applaud()
    {
        System.out.println("CLAP CLAP CLAP CLAP");
    }

    @AfterThrowing("performance()")
    public void demandRefund()
    {
        System.out.println("Boo!We want our money back!");
    }

}

@Pointcut註解用於定義一個可以在@AspectJ切面內可重用的切點。
切點的名稱來源於註解所應用的方法名稱。因此,該切點的名稱爲performance()。
因爲AudienceOfAspectJ類本身包含了所有它需要定義的切點和通知,所有我們不再需要在XML配置中聲明切點和通知。我們需要在Spring上下文中聲明一個自動代理Bean,該Bean知道如何把@AspectJ註解所標註的Bean轉變爲代理通知。
爲此,Spring自帶了名爲AnnotationAwareAspectJAutoProxyCreator的自動代理創建類。我們可以在Spring上下文中把AnnotationAwareAspectJAutoProxyCreator註冊爲一個Bean。
爲了簡化如此長的名字,Spring在aop命名空間中提供了一個自定義的配置元素:<aop:aspectj-autoproxy />
我們需要記住<aop:aspectj-autoproxy />僅僅使用@AspectJ註解作爲指引來創建基於代理的切面,但本質上它仍然是一個Spring風格的切面。這意味着雖然我們使用@AspectJ的註解,但是我們仍然限於代理方法的調用。如果想利用AspectJ的所有能力,我們必須在運行時使用AspectJ並不依賴Spring來創建基於代理的切面。
<aop:aspect>相對於@AspectJ的一個明顯優勢是我們不需要實現切面功能的源碼。通過@AspectJ,我們必須標註類和方法,它需要有源碼。而<aop:aspect>可以引用任意一個Bean。
1.註解環繞通知

    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint joinpoint)
    {
        try
        {
            System.out.println("The audience is taking their seats.");
            System.out.println("The audienct is turning off their cellphones.");
            long start = System.currentTimeMillis();
            joinpoint.proceed();
            long end = System.currentTimeMillis();
            System.out.println("CLAP CLAP CLAP CLAP");
            System.out.println("The performance took " + (end - start) + " milliseconds.");
        }
        catch (Throwable e)
        {
            System.out.println("Boo!We want out money back!");
        }
    }

2.傳遞參數給標註的通知

package com.springinaction.springidol;


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;


@Aspect
public class MagicianOfAspectJ implements MindReader
{

    private String thoughts;

    @Pointcut("execution(* com.springinaction.springidol.Thinker.thinkOfSomething(String)) && args(thoughts)")
    public void thinking(String thoughts)
    {

    }

    @Before("thinking(thoughts)")
    public void interceptThoughts(String thoughts)
    {
        System.out.println("Intercepting volunteer's thoughts");
        this.thoughts = thoughts;
    }

    public String getThoughts()
    {
        return thoughts;
    }

}

3.標註引入
等價於<aop:declare-parents>的註解是@AspectJ的@DeclareParents。

package com.springinaction.springidol;


import org.aspectj.lang.annotation.DeclareParents;


public class ContestantIntroducer
{

    @DeclareParents(value = "com.springinaction.springidol.Performer", defaultImpl = GraciousContestant.class)
    public static Contestant contestant;
}
    <bean class="com.springinaction.springidol.ContestantIntroducer" />

ContestantIntroducer是一個切面。但是不同於我們之前所創建的那些切面,該切面並沒有提供前置、後置或環繞通知。它爲Performer Bean引入了Contestant接口。
value屬性等同於<aop:declare-parents>的types-matching屬性。它標識應該被引入指定接口的Bean的類型。
defaultImpl屬性等同於<aop:declare-parents>的default-impl屬性。它標識該類提供了所引入接口的實現。
由@DeclareParents註解所標註的static屬性指定了將被引入的接口。
當發現使用@Aspect註解所標註的Bean時,<aop:aspectj-autoproxy />將自動創建代理。依據被調用的方式是屬於被代理的Bean還是引入的接口,該代理把調用委託給被代理的Bean或引入的實現。
我們需要關注的一個注意事項是@DeclareParents沒有對應於<aop:declare-parents>的delegate-ref屬性所對應的等價物。這是因爲@DeclareParents是一個@AspectJ註解。@AspectJ是一個不同於Spring的項目,因此它的註解並不瞭解Spring的Bean。這意味着如果我們想委託給Spring所配置的Bean,那@DeclareParents並不能滿足需求,我們只能藉助於<aop:declare-parents>
Spring AOP有助於把橫切關注點從應用的業務邏輯中分離出來。但是正如我們所看到的,Spring切面仍然只是基於代理的,而且限於通知方法的調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章