4.4.2 声明环绕通知

目前 Audience 的实现工作得非常棒,但是前置通知和后置通知有一些限制。具体来说,如果不使用成员变量存储信息的话,在前置通知和后置通知之间共享信息非常麻烦。

例如,假设除了进场关闭手机和表演结束后鼓掌,我们还希望观众确保一直关注演出,并报告每个参赛者表演了多长时间。使用前置通知和后置通知实现该功能的唯一方式是在前置通知中记录开始时间并在某个后置通知中报告表演耗费的时间。但这样的话我们必须在一个成员变量中保存开始时间。因为 Audience 是单例的,如果像这样保存状态的话,将会存在线程安全问题。

相对于前置通知和后置通知,环绕通知在这点上有明显的优势。使用环绕通知,我们可以完成前置通知和后置通知所实现的相同功能,而且只需要在一个方法中实现。因为整个通知逻辑是在一个方法内实现的,所以不需要使用成员变量保存状态。

例如,考虑程序清单 4.11 中新 Audience 类的 watchPerformance() 方法,它没有使用任何的注解。

程序清单 4.11 watchPerformance() 方法提供了 AOP 环绕通知
package concert;

import org.aspectj.lang.ProceedingJoinPoint;

public class Audience {

  public void watchPerformance(ProceedingJoinPoint jp) {
    try {
      System.out.println("Silencing cell phones");
      System.out.println("Taking seats");
      jp.procee();
      System.out.println("CLAP CLAP CLAP!!!");
    } catch (Throwable e) {
      System.out.println("Demanding a refund");
    }
  }
}

在观众切面中,watchPerformance() 方法包含了之前四个通知方法的所有功能。不过,所有的功能都放在了这一个方法中,因此这个方法还要负责自身的异常处理。

声明环绕通知与声明其他类型的通知并没有太大区别。我们所需要做的仅仅是使用 <aop:around> 元素。

程序清单 4.12 在 XML 中使用元素声明环绕通知
<aop:config>
  <aop:aspect ref="audience">
    <aop:pointcut
      id="performance"
      expression="execution(** concert.Performance.perform(..))" />
    
    <aop:around
      pointcut-ref="performance"
      method="watchPerformance" />
      
  </aop:aspect>
</aop:config>

像其他通知的 XML 元素一样,<aop:around> 指定了一个切点和一个通知方法的名字。在这里,我们使用跟之前一样的切点,但是为该切点所设置的 method 属性值为 watchPerformance() 方法。

Last updated