> For the complete documentation index, see [llms.txt](https://potoyang.gitbook.io/spring-in-action-v4/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://potoyang.gitbook.io/spring-in-action-v4/untitled/untitled-2/untitled-1.md).

# 4.4.1　声明前置和后置通知

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

```markup
<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 展示了通知逻辑如何织入到业务逻辑中。

![图 4.8　Audience 切面包含四种通知，它们把通知逻辑织入进匹配切面切点的方法中](/files/-Lmn377KqbI62C-LKH1L)

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

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

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

```markup
<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> 元素的范围内。
