Spring AOP 完整详解
一、什么是 AOP
1. 全称
AOP:Aspect Oriented Programming 面向切面编程
2. 核心思想
纵向抽取重复通用逻辑,横向切入业务代码,解耦
- OOP(面向对象):纵向划分模块(分层:Controller/Service/Mapper),解决业务核心逻辑;
- AOP(面向切面):横向抽取通用非业务逻辑(日志、事务、权限),统一管理,不侵入原有业务代码。
3. 解决的痛点
传统开发:每个业务方法都重复写日志、事务、异常捕获,代码冗余、耦合严重、修改麻烦。 AOP 方案:把通用逻辑抽成切面,在不改动业务代码前提下,自动切入所有目标方法。
二、AOP 七大核心概念(必背)
1. JoinPoint 连接点
程序中可以被切面拦截的点,Spring AOP 仅支持方法执行作为连接点(不支持字段、构造器)。 例:Service 层所有业务方法都是连接点。
2. Pointcut 切点
筛选匹配的连接点,通过表达式规则精准定位要拦截哪些方法。 常用切点表达式:
java
运行
// 拦截com.service包下所有类的所有方法 execution(* com.service.*.*(..))连接点是全部候选,切点是筛选后的子集。
3. Advice 通知(增强逻辑)
切面具体要执行的通用代码,在切点方法的不同时机执行,分 5 种:
- @Before 前置通知:目标方法执行之前执行(权限校验、参数日志)
- @AfterReturning 返回通知:目标方法正常执行完毕、无异常后执行(返回值日志)
- @AfterThrowing 异常通知:目标方法抛出异常时执行(异常告警、错误日志)
- @After 后置通知:目标方法无论正常 / 异常,最终都会执行(资源释放)
- @Around 环绕通知:包裹目标方法,手动控制目标方法何时执行、是否执行(最强大,事务、耗时统计)
4. Aspect 切面
切点 + 通知 的结合体,一个切面 = 拦截规则(Pointcut)+ 通用增强逻辑(Advice) 开发中就是一个加了@Aspect的类。
5. Target 目标对象
被切面拦截、增强的原始业务 Bean 对象。
6. Proxy 代理对象
AOP 底层通过动态代理生成的代理对象,容器实际注入使用的是代理对象,代理对象包含目标方法 + 切面增强逻辑。 Spring 两种代理实现:
- JDK 动态代理:目标类实现了接口,基于接口代理;
- CGLIB 代理:目标类没有实现接口,继承目标类生成子类代理; SpringBoot2.x 默认使用 CGLIB。
7. Weaving 织入
把切面的通知逻辑,动态植入到目标对象方法的过程。 Spring AOP 是运行时织入(容器创建 Bean 时生成代理完成织入)。
三、五种通知执行顺序
- @Around 环绕前置代码
- @Before 前置通知
- 执行目标业务方法
- @Around 环绕后置代码
- 分两种分支:
- 方法正常返回:@After → @AfterReturning
- 方法抛出异常:@After → @AfterThrowing
四、AOP 典型应用场景(工作 + 面试高频)
1. 统一日志处理(最常用)
记录接口入参、出参、方法执行耗时、调用人,不用每个方法手写日志。
2. 事务管理(Spring 核心场景)
@Transactional底层就是 AOP 环绕通知: 方法执行前开启事务,正常执行提交事务,出现异常回滚事务。
3. 接口权限校验
拦截 Controller 接口,校验 token、登录状态、接口访问权限,无权限直接拦截,不执行业务。
4. 全局异常统一处理
捕获所有业务方法抛出的异常,统一封装返回结果、记录错误日志、发送异常告警。
5. 接口限流、防重复提交
通过 AOP 拦截接口,结合 Redis 实现接口限流、判断重复提交请求。
6. 数据脱敏
返回前端的数据自动脱敏(手机号、身份证、银行卡隐藏部分数字)。
7. 缓存统一处理
方法执行前查询缓存,有缓存直接返回;无缓存执行业务,结果存入缓存。
五、简单代码示例(注解 AOP)
java
运行
// 1. 切面类 @Aspect @Component public class LogAspect { // 切点:拦截service包下所有方法 @Pointcut("execution(* com.service.*.*(..))") public void pointCut(){} // 环绕通知,统计方法耗时 @Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); // 执行目标方法 Object result = joinPoint.proceed(); long cost = System.currentTimeMillis() - start; System.out.println("方法执行耗时:" + cost + "ms"); return result; } }六、高频面试延伸考点
- JDK 代理和 CGLIB 区别
- JDK:要求目标类实现接口,代理接口方法,速度快;
- CGLIB:无需接口,继承目标类生成子类,无法代理 final 类 / 方法。
- AOP 和拦截器、过滤器区别
- Filter 过滤器:Servlet 层面,只拦截 http 请求,不属于 Spring;
- Interceptor 拦截器:SpringMVC 层面,拦截 Controller;
- AOP 切面:Spring Bean 层面,可拦截 Controller、Service、Mapper 所有 Bean 方法,粒度更细。
- 为什么循环依赖场景 AOP 代理提前生成?正常 Bean 在初始化完成后生成代理;存在循环依赖时,为保证注入的是代理对象,提前通过三级缓存 ObjectFactory 创建代理。
