本篇博客将从 AOP 的核心术语入手,手把手带你完成 Spring 中基于 XML 配置文件的 AOP 实现,让你彻底掌握 AOP 的配置方式与通知类型。
一、AOP 核心术语详解
在正式学习配置之前,我们必须先搞懂 AOP 中的几个核心术语,这是后续配置的基础:
| 术语 | 英文 | 说明 |
|---|---|---|
| 连接点 | Joinpoint | 指程序中被拦截到的点,在 Spring 中特指方法,因为 Spring 只支持方法级别的拦截。 |
| 切入点 | Pointcut | 对哪些 Joinpoint 进行拦截的定义,用来匹配需要被增强的方法。 |
| 通知 / 增强 | Advice | 拦截到 Joinpoint 之后要执行的操作,也就是增强的逻辑。分为前置、后置、异常、最终、环绕通知。 |
| 目标对象 | Target | 被代理的原始对象,也就是我们要增强的业务类对象。 |
| 织入 | Weaving | 将增强逻辑应用到目标对象,创建新的代理对象的过程。 |
| 代理 | Proxy | 目标对象被 AOP 织入增强后,生成的代理类对象。 |
| 切面 | Aspect | 切入点 + 通知的结合,是我们需要编写和配置的核心。 |
简单来说,切面就是 “在哪里(切入点)”+“做什么(通知)” 的完整定义。
二、环境准备:创建 Maven 项目并引入依赖
首先,我们创建一个新的 Maven 项目 SpringAOP02,并在 pom.xml 中引入 AOP 相关依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qcby</groupId><artifactId>SpringAOP02</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- Spring 核心依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><!-- 日志依赖 --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><!-- Spring 测试依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><!-- JUnit 单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!-- AOP 联盟规范依赖 --><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!-- Spring Aspects 依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><!-- AspectJ Weaver 依赖 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><!-- 源代码级别 --><source>1.8</source><!-- 目标字节码级别 --><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build></project>
关键依赖说明:
-
aopalliance:AOP 联盟定义的标准接口,Spring AOP 基于此规范实现。
-
spring-aspects:Spring 对 AspectJ 的集成支持。
-
aspectjweaver:AspectJ 的织入器,提供切入点表达式解析和代理生成功能。
三、项目结构与核心文件
3.1 项目整体结构
SpringAOP02
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── qcby
│ │ │ └── demo2
│ │ │ ├── UserService.java // 业务接口
│ │ │ ├── UserServiceImpl.java // 业务实现类(目标对象)
│ │ │ └── MyXmlAspect.java // 切面类(增强逻辑)
│ │ └── resources
│ │ └── applicationContext_demo2.xml // Spring AOP 配置文件
│ └── test
│ └── java
│ └── com
│ └── qcby
│ └── test
│ └── Demo2.java // 单元测试类
└── pom.xml
3.2 业务接口与实现类(目标对象)
3.2.1 UserService.java(业务接口)
package com.qcby.demo2;public interface UserService {void save();
}
3.2.2 UserServiceImpl.java(业务实现类)
package com.qcby.demo2;import com.qcby.demo2.UserService;public class UserServiceImpl implements UserService {@Overridepublic void save() {System.out.println("执行了UserService的save方法...");// 模拟业务异常,用于测试异常通知// int i = 1 / 0;}
}
说明: 这是我们的目标对象,save() 方法是我们要增强的业务方法。
3.3 切面类(增强逻辑)
MyXmlAspect.java 是我们自定义的切面类,里面包含了各种通知方法:
package com.qcby.demo2;import org.aspectj.lang.ProceedingJoinPoint;/*** 自定义切面类 = 切入点(表达式) + 通知(增强的代码)*/
public class MyXmlAspect {/*** 前置通知:目标方法执行前执行*/public void beforeLog() {System.out.println("[前置通知]:目标方法执行前,记录日志...");}/*** 最终通知:无论目标方法成功还是失败,都会执行*/public void afterLog() {System.out.println("[最终通知]:目标方法执行后,释放资源...");}/*** 后置通知:目标方法执行成功后执行(如果方法异常则不执行)*/public void afterReturningLog() {System.out.println("[后置通知]:目标方法执行成功,记录返回结果...");}/*** 异常通知:目标方法执行失败时执行*/public void afterThrowingLog() {System.out.println("[异常通知]:目标方法执行失败,记录异常信息...");}/*** 环绕通知:目标方法执行前后都可以增强,需要手动调用目标方法*/public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {System.out.println("[环绕通知-前置]:目标方法执行前...");// 手动调用目标方法Object result = pjp.proceed();System.out.println("[环绕通知-后置]:目标方法执行后...");return result;}
}
3.4 Spring AOP 配置文件 applicationContext_demo2.xml
3.4.1 引入 AOP 命名空间
首先,在配置文件的根标签中引入 AOP 的 schema 约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 后续配置写在这里 --></beans>
3.4.2 配置目标对象和切面类
<!-- 1. 配置目标对象:业务实现类 --><bean id="userService" class="com.qcby.demo2.UserServiceImpl"/><!-- 2. 配置切面类:增强逻辑所在的类 --><bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>
3.4.3 配置 AOP 增强(核心配置)
<!-- 3. 配置 AOP 增强 --><aop:config><!-- 配置切面:ref 指定切面类 --><aop:aspect ref="myXmlAspect"><!-- 切入点表达式格式:execution([修饰符] 返回值类型 包名.类名.方法名(参数))常用通配符:*:匹配任意修饰符、返回值、类名、方法名..:匹配任意参数列表--><!-- 前置通知:save()方法执行前执行beforeLog() --><aop:before method="beforeLog"pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/><!-- 最终通知:save()方法执行后执行afterLog() --><aop:after method="afterLog"pointcut="execution(* com.qcby.*.*Service*.save(..))"/><!-- 后置通知:save()方法执行成功后执行afterReturningLog() --><aop:after-returning method="afterReturningLog"pointcut="execution(* com.qcby.*.*Service*.save(..))"/><!-- 异常通知:save()方法执行失败时执行afterThrowingLog() --><aop:after-throwing method="afterThrowingLog"pointcut="execution(* com.qcby.*.*Service*.save(..))"/><!-- 环绕通知:目标方法执行前后都增强 --><aop:around method="aroundLog"pointcut="execution(* com.qcby.*.*Service*.save(..))"/></aop:aspect></aop:config>
3.5 单元测试类 Demo2.java
package com.qcby.test;import com.qcby.demo2.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_demo2.xml")
public class Demo2 {@Autowiredprivate UserService userService;@Testpublic void run1() {// 调用被增强的方法userService.save();}
}
运行 Demo2 中的 run1() 方法,控制台输出如下:

如果我们取消 UserServiceImpl 中 int i = 1 / 0; 的注释,模拟业务异常,输出如下:
结果说明:
-
正常执行时,前置通知 → 目标方法 → 环绕后置 → 后置通知 → 最终通知 依次执行。
-
出现异常时,前置通知 → 异常通知 → 最终通知 执行,后置通知不会执行。
四、关键知识点详解
4.1 切入点表达式详解
切入点表达式用于定义 “要增强哪些方法”,格式为:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
-
修饰符: 可选,如 public,可省略或用 * 匹配任意修饰符。
-
返回值类型: 不可省略,可用 * 匹配任意返回值类型。
-
包名: 不可省略,com.qcbyjy.* 表示 com.qcbyjy 下的任意子包;com.qcbyjy.. 表示 com.qcbyjy 及其所有子包。
-
类名: 可用 * 匹配,如 Service 匹配所有包含 Service 的类名。
-
方法名: 可用 * 匹配,如 save* 匹配所有以 save 开头的方法。
-
参数: () 表示无参;(..) 表示任意参数列表;(*) 表示任意一个参数。
通用写法示例:
<!-- 匹配 com.qcbyjy 包下所有以 ServiceImpl 结尾的类中,以 save 开头的任意参数方法 -->
execution(* com.qcbyjy.*.*ServiceImpl.save*(..))
4.2 AOP 的 5 种通知类型
| 通知类型 | 标签 | 执行时机 | 说明 |
|---|---|---|---|
| 前置通知 | aop:before | 目标方法执行前 | 常用于日志、权限校验 |
| 最终通知 | aop:after | 目标方法执行后(无论成功 / 失败) | 常用于释放资源 |
| 后置通知 | aop:after-returning | 目标方法执行成功后 | 常用于记录返回结果 |
| 异常通知 | aop:after-throwing | 目标方法执行失败时 | 常用于异常日志记录 |
| 环绕通知 | aop:around | 目标方法执行前后 | 可手动控制目标方法的执行,功能最强大 |
注意: 后置通知和异常通知是互斥的,只有目标方法执行成功时,后置通知才会执行;只有目标方法抛出异常时,异常通知才会执行。
六、总结
通过本篇博客,我们完成了 Spring AOP 基于 XML 配置文件的完整实现:
-
引入了 AOP 相关依赖,配置了切面类和目标对象。
-
掌握了切入点表达式的写法,能灵活匹配需要增强的方法。
-
了解了 5 种通知类型的执行时机和使用场景。
-
成功测试了正常和异常场景下的通知执行流程。
这种 XML 配置方式虽然直观,但在实际开发中,注解方式会更加简洁高效。下一篇博客,我们将介绍 Spring AOP 的注解实现方式!
