--- title: Spring AOP简介 date: 2025-12-25 --- # 一、基本概念   AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,核心思想是**将与业务核心逻辑无关但又被多个模块共享的功能(如日志、事务、权限校验、性能监控)抽取出来,形成独立的 “切面”**,在不修改原有业务代码的前提下,通过 “织入” 的方式动态添加到目标方法的执行流程中。   Spring AOP 是 Spring 框架对 AOP 思想的实现,基于**动态代理(JDK 动态代理 + CGLIB 动态代理)**,专注于解决企业级开发中的横切关注点问题。 | 术语 | 说明 | |------|------| | 切面(Aspect) | 封装横切逻辑的类(如日志切面、事务切面),包含通知和切点 | | 连接点(Joinpoint) | 程序执行过程中的某个特定点(如方法调用、异常抛出),Spring AOP 中仅支持方法级连接点 | | 切点(Pointcut) | 匹配连接点的规则,用于指定切面要织入到哪些方法上 | | 通知(Advice) | 切面的具体逻辑(如日志打印、权限校验),按执行时机分为 5 种 | | 目标对象(Target) | 被切面织入的目标类(业务类) | | 代理对象(Proxy) | Spring AOP 为目标对象创建的代理类,横切逻辑通过代理执行 | | 织入(Weaving) | 将切面逻辑添加到目标对象的过程,Spring AOP 是运行时织入(通过动态代理) | | 通知类型 | 执行时机 | |----------|----------| | 前置通知(Before) | 目标方法执行前执行 | | 后置通知(After) | 目标方法执行后执行(无论是否抛出异常) | | 返回通知(AfterReturning) | 目标方法正常返回后执行 | | 异常通知(AfterThrowing) | 目标方法抛出异常后执行 | | 环绕通知(Around) | 包裹目标方法执行,可自定义目标方法执行前后的逻辑(功能最强) |   [AOP API](https://docs.springjava.cn/spring-framework/reference/core/aop-api.html) # 二、原理   Spring AOP 基于动态代理实现,分为两种方式: 1. JDK 动态代理 通过 java.lang.reflect.Proxy 生成代理类,代理类实现目标接口,调用 InvocationHandler 处理横切逻辑;基于接口,无侵入。 2. CGLIB 动态代理 通过继承目标类生成子类作为代理类,重写目标方法,织入横切逻辑;基于继承,目标类不能是 final,方法不能是 final。   Spring AOP 自动选择规则:如果目标类实现了接口,使用 JDK 动态代理;否则使用 CGLIB。 # 三、实战   例如存在以下业务模块: ```java @Service public class OrderService { // 模拟创建订单 public String createOrder(String orderNo) { System.out.println("创建订单:" + orderNo); return "订单创建成功:" + orderNo; } // 模拟删除订单(故意抛出异常) public void deleteOrder(String orderNo) { System.out.println("删除订单:" + orderNo); throw new RuntimeException("删除订单失败:订单不存在"); } } ```   AOP实现: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.Arrays; // 1. 标记为切面类 @Aspect // 2. 交给Spring容器管理 @Component public class LogAspect { // 3. 定义切点:匹配OrderService类的所有方法 @Pointcut("execution(* com.example.aopdemo.service.OrderService.*(..))") public void orderServicePointcut() {} // 4. 前置通知:目标方法执行前执行 @Before("orderServicePointcut()") public void beforeAdvice(JoinPoint joinPoint) { // 获取方法名和参数 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); System.out.println("[前置通知] 方法:" + methodName + ",参数:" + Arrays.toString(args)); } // 5. 后置通知:目标方法执行后执行(无论是否异常) @After("orderServicePointcut()") public void afterAdvice(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("[后置通知] 方法:" + methodName + " 执行完毕"); } // 6. 返回通知:目标方法正常返回后执行 @AfterReturning(value = "orderServicePointcut()", returning = "result") public void afterReturningAdvice(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); System.out.println("[返回通知] 方法:" + methodName + ",返回值:" + result); } // 7. 异常通知:目标方法抛出异常后执行 @AfterThrowing(value = "orderServicePointcut()", throwing = "ex") public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) { String methodName = joinPoint.getSignature().getName(); System.out.println("[异常通知] 方法:" + methodName + ",异常:" + ex.getMessage()); } // 8. 环绕通知:包裹目标方法执行(功能最强) @Around("orderServicePointcut()") public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { String methodName = proceedingJoinPoint.getSignature().getName(); Object[] args = proceedingJoinPoint.getArgs(); // 前置逻辑 System.out.println("[环绕通知-前置] 方法:" + methodName + ",参数:" + Arrays.toString(args)); Object result = null; try { // 执行目标方法 result = proceedingJoinPoint.proceed(); // 返回后逻辑 System.out.println("[环绕通知-返回] 方法:" + methodName + ",返回值:" + result); } catch (Throwable e) { // 异常逻辑 System.out.println("[环绕通知-异常] 方法:" + methodName + ",异常:" + e.getMessage()); throw e; // 必须抛出异常,否则上层无法捕获 } finally { // 后置逻辑 System.out.println("[环绕通知-后置] 方法:" + methodName + " 执行完毕"); } return result; } } ```   测试: ```java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class OrderServiceTest { @Autowired private OrderService orderService; // 测试正常方法(createOrder) @Test public void testCreateOrder() { orderService.createOrder("ORDER_20251225"); } // 测试异常方法(deleteOrder) @Test public void testDeleteOrder() { try { orderService.deleteOrder("ORDER_9999"); } catch (Exception e) { // 捕获异常,避免测试中断 } } } ```   测试结果: ```plaintext [环绕通知-前置] 方法:createOrder,参数:[ORDER_20251225] [前置通知] 方法:createOrder,参数:[ORDER_20251225] 创建订单:ORDER_20251225 [环绕通知-返回] 方法:createOrder,返回值:订单创建成功:ORDER_20251225 [返回通知] 方法:createOrder,返回值:订单创建成功:ORDER_20251225 [后置通知] 方法:createOrder 执行完毕 [环绕通知-后置] 方法:createOrder 执行完毕 [环绕通知-前置] 方法:deleteOrder,参数:[ORDER_9999] [前置通知] 方法:deleteOrder,参数:[ORDER_9999] 删除订单:ORDER_9999 [环绕通知-异常] 方法:deleteOrder,异常:删除订单失败:订单不存在 [异常通知] 方法:deleteOrder,异常:删除订单失败:订单不存在 [后置通知] 方法:deleteOrder 执行完毕 [环绕通知-后置] 方法:deleteOrder 执行完毕 ``` # 四、切点表达式 | 切点表达式 | 说明 | |------------|------| | `execution(* com.example.service.*.*(..))` | 匹配 com.example.service 包下所有类的所有方法 | | `execution(public String com.example.service.OrderService.create*(String))` | 匹配 OrderService 类中公共、返回值为 String、方法名以 create 开头、参数为 String 的方法 | | `execution(* com.example..*Service.*(..))` | 匹配 com.example 包及其子包下所有以 Service 结尾的类的所有方法 | | `@annotation(com.example.annotation.Log)` | 匹配标注了 @Log 自定义注解的方法 |   组合切点: ```java // 匹配OrderService的方法 且 方法名包含"Order" @Pointcut("execution(* com.example.service.OrderService.*(..)) && execution(* *Order*(..))") public void combinePointcut() {} ``` # 五、总结   通俗的解释,AOP本质是:找目标(通过切点规则,精准找到要 “加戏” 的方法 / 类)→ 造代理(Spring 自动给这些目标生成一个 “中间商”(代理对象))→ 加戏(在目标方法真正执行前 / 后 / 出问题时,让这个 “中间商” 帮你干额外的活)。 ::: tip 基于 Spring AOP 的知名框架/组件汇总 | 框架/组件 | 核心作用 | Spring AOP 应用说明 | |-----------|----------|---------------------| | AspectJ | 提供切点表达式语法、注解解析(@Aspect/@Pointcut 等) | Spring AOP 仅复用其语法规则,底层仍为自身动态代理(未使用 AspectJ 编译期织入) | | Spring Core | 容器管理、动态代理创建(JDK/CGLIB) | 负责切面/目标类的生命周期管理,生成代理对象并完成织入逻辑 | | Spring Transaction | 声明式事务管理 | 切点:匹配 @Transactional 注解方法;
通知:方法执行前开启事务、执行后提交、异常时回滚 | | Spring Cache | 声明式缓存管理 | 切点:匹配 @Cacheable/@CacheEvict 等注解方法;
通知:方法执行前查缓存、执行后更新缓存 | | spring-boot-starter-aop | AOP 基础依赖封装 | 自动引入 AspectJ + Spring AOP 核心,开箱即用 | | Spring Security | 安全权限管理 | 切点:匹配 @PreAuthorize 等权限注解方法;
通知:方法执行前校验用户权限,无权限抛异常 | | MyBatis-Plus | 增强 MyBatis CRUD 操作 | 切点:匹配 Mapper 接口方法/标注注解的实体类;
通知:织入分页逻辑、注解(@TableName)解析等 | | Shiro | 权限管理框架 | 切点:匹配 @RequiresPermissions 注解方法;
通知:方法执行前校验用户权限 | | Seata | 分布式事务框架 | 切点:匹配 @GlobalTransactional 注解方法;
通知:织入全局事务的开启、提交、回滚逻辑 | | Sentinel | 流量控制/熔断降级框架 | 切点:匹配 @SentinelResource 注解方法;
通知:方法执行前校验流量阈值、熔断规则 | | Dynamic-TP | 动态线程池框架 | 切点:匹配线程池执行方法;
通知:织入线程池耗时、队列长度等监控逻辑 | | p6spy | 数据库 SQL 监控 | 切点:拦截 JDBC 操作方法;
通知:织入 SQL 打印、执行耗时统计逻辑 | | dynamic-datasource-spring-boot-starter | 动态数据源切换 | 切点:匹配 @DS 注解方法;
通知:方法执行前切换数据源、执行后恢复默认数据源 | | xxl-job | 分布式任务调度 | 切点:匹配任务执行方法;
通知:织入任务执行日志、监控指标采集逻辑 | | Spring Cloud Sleuth | 微服务链路追踪 | 切点:拦截 HTTP/RPC 调用方法;
通知:织入 traceId/spanId 传递、链路信息采集逻辑 | :::