4.4.1 声明前置和后置通知

你可以再把那些 AspectJ 注解加回来,但这并不是本节的目的。相反,我们会使用 Spring aop 命名空间中的一些元素,将没有注解的 Audience 类转换为切面。下面的程序展示了所需要的 XML。

<aop:config>
  <aop:aspect ref="audience">
  
    <aop:before
      pointcut="execution(** concert.Performance.perform(..))"
      method="silenceCellPhones" />
    
    <aop:before
      pointcut="execution(** concert.Performance.perform(..))"
      method="takeSeats" />
      
    <aop:after-returning 
      pointcut="execution(** concert.Performance.perform(..))"
      method="applause" />
      
    <aop:after-throwing
      pointcut="execution(** concert.Performance.perform(..))"
      method="demandRefund" />
      
  </aop:aspect>
</aop:config>

关于 Spring AOP 配置元素,第一个需要注意的事项是大多数的 AOP 配置元素必须在 <aop:config> 元素的上下文内使用。这条规则有几种例外场景,但是把 bean 声明为一个切面时,我们总是从 <aop:config> 元素开始配置的。

在 <aop:config> 元素内,我们可以声明一个或多个通知器、切面或者切点。在上述程序中,我们使用 <aop:aspect> 元素声明了一个简单的切面。ref 元素引用了一个 POJO bean,该 bean 实现了切面的功能 —— 在这里就是 audience。ref 元素所引用的 bean 提供了在切面中通知所调用的方法。

该切面应用了四个不同的通知。两个 <aop:before> 元素定义了匹配切点的方法执行之前调用前置通知方法,也就是 Audience bean 的 takeSeats() 和 turnOffCellPhones() 方法(由 method 属性所声明)。<aop:after-returning> 元素定义了一个返回(after-returning)通知,在切点所匹配的方法调用之后再调用 applaud() 方法。同样,元素定义了异常(after-throwing)通知,如果所匹配的方法执行时抛出任何的异常,都将会调用 demandRefund() 方法。图 4.8 展示了通知逻辑如何织入到业务逻辑中。

在所有的通知元素中,pointcut 属性定义了通知所应用的切点,它的值是使用 AspectJ 切点表达式语法所定义的切点。

你或许注意到所有通知元素中的 pointcut 属性的值都是一样的,这是因为所有的通知都要应用到相同的切点上。

在基于 AspectJ 注解的通知中,当发现这种类型的重复时,我们使用 @Pointcut 注解消除了这些重复的内容。而在基于 XML 的切面声明中,我们需要使用元素。如下的 XML 展现了如何将通用的切点表达式抽取到一个切点声明中,这样这个声明就能在所有的通知元素中使用了。

<aop:config>
  <aop:aspect ref="audience">
    <aop:pointcut
      id="performance"
      expressions="execution(** concert.Performance.perform(..))" />
  
    <aop:before
      pointcut-ref="performance"
      method="silenceCellPhones" />
    
    <aop:before
      pointcut-ref="performance"
      method="takeSeats" />
      
    <aop:after-returning 
      pointcut-ref="performance"
      method="applause" />
      
    <aop:after-throwing
      pointcut-ref="performance"
      method="demandRefund" />
      
  </aop:aspect>
</aop:config>

现在切点是在一个地方定义的,并且被多个通知元素所引用。<aop:pointcut> 元素定义了一个 id 为 performance 的切点。同时修改所有的通知元素,用 pointcut-ref 属性来引用这个命名切点。

正如上述程序所展示的,<aop:pointcut> 元素所定义的切点可以被同一个 <aop:aspect> 元素之内的所有通知元素引用。如果想让定义的切点能够在多个切面使用,我们可以把 <aop:pointcut> 元素放在 <aop:config> 元素的范围内。

Last updated