Spring AOP通知類型介紹

原文地址:http://www.baeldung.com/spring-aop-advice-tutorial

概述

這遍文章將要討論Spring AOP中使用到的各種通知類型,
通知是切面的具體邏輯實現,由切面負責執行,具體的某個通知對應具體的某些由切點描述的連接點。通知類型包括:前置、後置、環繞。切面即在由對象構建成的分層結構中橫向關注點的抽像,比如:日誌、系統配置、緩存、數據庫事務管理等。
如果你想深入瞭解切點表達式,請查看我的上一篇翻譯文章:

啓用通知

在Spring裏,在使用AspectJ提供的註解聲明通知之前,首先在Spring的配置類裏聲明@EnableAspectJAutoProxy 啓用AspectJ組件,之後,Spring AOP將掃描和管理被AspectJ的@Aspect註解過的切面bean。

@Configuration
@ComponentScan(basePackages = {"org.baeldung.dao", "org.baeldung.aop"})
@EnableAspectJAutoProxy
public class TestConfig {
    ...
}

前置通知

顧明思義,前置通知在連接點被執行前就被執行了,除非拋出異常,否則前置通知不能夠阻止方法的繼續執行。
請看下面的代碼片段,這個切面簡單地打印了方法名稱,在連接點執行之前。

@Component
@Aspect
public class LoggingAspect {

    private Logger logger = Logger.getLogger(LoggingAspect.class.getName());

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {};

    @Before("repositoryMethods()")
    public void logMethodCall(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        logger.info("Before " + methodName);
    }
}

在切點表達式repositoryMethods匹配的repository method連接點執行之前,前置通知logMethodCall方法將會首先被執行。

後置通知

使用@After註解來聲明後置通知。在被匹配的連接點執行完後,執行後置通知,不管連接點執行是否有拋出異常.從另一個角度來看,後置通知跟finally語句塊相似。如果你只是想在連接點方法正常返回時執行通知(異常時不執行通知),應該使用@AfterReturning註解聲明的返回型通知。如果你只是想在連接點方法拋出異常後執行通知(正常返回時不執行),應該使用@AfterThrowing註解聲明的異常通知。

假設當一個新的Foo的實例被創建時,我們希望觸發一個通知事件,通知監控的組件,我們可以從FooDao發佈一個事件,但是這會破壞對象的單一責任原則,這時面向切面遍程可以提供另一種方式,我們可以通過定義下面的這樣一個切面來達到這種效果。

@Component
@Aspect
public class PublishingAspect {

    private ApplicationEventPublisher eventPublisher;

    @Autowired
    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {}

    @Pointcut("execution(* *..create*(Long,..))")
    public void firstLongParamMethods() {}

    @Pointcut("repositoryMethods() && firstLongParamMethods()")
    public void entityCreationMethods() {}

    @AfterReturning(value = "entityCreationMethods()", returning = "entity")
    public void logMethodCall(JoinPoint jp, Object entity) throws Throwable {
        eventPublisher.publishEvent(new FooCreationEvent(entity));
    }
}

請注意:首先,@AfterReturning註解,我們可以訪問方法的返回值。其實,通過聲明JoinPoint參數,我們可以使用目標方法調用的參數。

下一步,我們定義一個監控器來簡單地記錄事件,更多關於事件的文檔,請查看:http://www.baeldung.com/spring-events

@Component
public class FooCreationEventListener implements ApplicationListener<FooCreationEvent> {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Override
    public void onApplicationEvent(FooCreationEvent event) {
        logger.info("Created foo instance: " + event.getSource().toString());
    }
}

環繞通知

環繞通知包圍着連接點,例如在目標方法調用前後執行切面邏輯。

環繞通知是最強大的一種通知,在目標方法執行的前後可以執行用戶自定義的切面邏輯,也可以在環繞通知裏決定是否繼續執行連接點方法或者跳過連接點直接在環繞通裏返回值或者拋出異常。

爲了演示環繞通知的使用例子,現在假如你想要測量連接點方法的執行時間,爲了達到這樣的目的,你可以建立以下這個的切面:

@Aspect
@Component
public class PerformanceAspect {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Pointcut("within(@org.springframework.stereotype.Repository *)")
    public void repositoryClassMethods() {};

    @Around("repositoryClassMethods()")
    public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        Object retval = pjp.proceed();
        long end = System.nanoTime();
        String methodName = pjp.getSignature().getName();
        logger.info("Execution of " + methodName + " took " + 
          TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
        return retval;
    }
}

當通過repositoryClassMethods 這個切點匹配的所有連接點被執行時,上面切面所示的通知就會被觸發。這個方法接收一個ProceedingJointPoint類型的參數,我們可以在使用這個參數執行目標方法之前,執行我們的切面邏輯。在上面的例子裏,我們只時簡單地保存了目標方法調用的開始時間。其次,由於目標方法可以返回任意類型的對象,所以這個通知的返回值類型是Object,有一點需要注意的是,如果目標方法返回類型是void,通知的必須返回一個null。當目標方法調用完成後,我們可以計算調用時間並打印,然後把返回值返回給調用者。

總結

在這篇文單裏,我們學習了Spring中的不同類型的通知和這些通知的聲明和實現方式。我們使用基於模式定義的方法和java註解來定義切面。我們提供了幾種合適的通知應用場景。

上面所有的例子實現和代碼可以在原作者的github裏找到:https://github.com/eugenp/tutorials/tree/master/spring-mvc-java ,那是基於eclipse的工程結構,所以很容易導入和運行。

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