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


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://potoyang.gitbook.io/spring-in-action-v4/untitled/untitled-2/untitled-1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
