下面给你一篇系统、实战向的《Spring AOP 详解》,覆盖原理 → 核心概念 → 注解/配置 → 执行顺序 → 常见场景 → 坑点,适合学习和面试复盘。
一、什么是 AOP?
AOP(Aspect Oriented Programming,面向切面编程)
是一种编程思想,用于将横切关注点(如日志、事务、权限、性能监控)从业务逻辑中分离出来。
👉 核心目标:
不修改原有业务代码,就能给方法“增强功能”
为什么需要 AOP?
传统写法的问题:
public void saveUser() {
log.info("开始");
// 业务逻辑
log.info("结束");
}
缺点:
- 大量重复代码
- 侵入业务逻辑
- 难以维护
AOP 解决方案:
👉 把日志、事务等统一抽出来
二、Spring AOP 的本质
一句话总结
Spring AOP = 动态代理 + 切点表达式
Spring AOP 只作用于方法级别(不像 AspectJ 可作用于字段、构造器)。
Spring AOP 两种代理方式
| 方式 | 说明 |
|---|---|
| JDK 动态代理 | 目标类 实现接口 |
| CGLIB 代理 | 目标类 没有接口(默认方式) |
spring.aop.proxy-target-class=true
强制使用 CGLIB
三、AOP 核心概念(必背)
| 概念 | 说明 |
|---|---|
| Aspect(切面) | 一个类,封装增强逻辑 |
| JoinPoint(连接点) | 方法执行的某个点 |
| Pointcut(切点) | 选中哪些方法 |
| Advice(通知) | 在何时做什么 |
| Target | 被代理对象 |
| Proxy | 代理对象 |
| Weaving | 织入过程 |
四、Advice(通知)类型
Spring 提供 5 种通知
| 通知 | 注解 | 说明 |
|---|---|---|
| 前置通知 | @Before | 方法执行前 |
| 后置通知 | @After | 方法执行后(无论异常) |
| 返回通知 | @AfterReturning | 正常返回 |
| 异常通知 | @AfterThrowing | 抛异常 |
| 环绕通知 | @Around | 最强,包裹整个方法 |
五、切点表达式(Pointcut)
最常用:execution
execution(访问修饰符 返回值 包名.类名.方法名(参数))
示例
execution(* com.example.service.*.*(..))
含义:
- 任意返回值
- service 包下所有类
- 所有方法
- 任意参数
常见写法速查
execution(* *..service..*(..))
execution(public * *(..))
@annotation(com.example.annotation.Log)
六、基于注解的 AOP(主流)
1️⃣ 启用 AOP
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {}
2️⃣ 定义切面
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {}
@Before("servicePointcut()")
public void before(JoinPoint jp) {
System.out.println("方法前:" + jp.getSignature().getName());
}
@AfterReturning(value = "servicePointcut()", returning = "result")
public void afterReturning(Object result) {
System.out.println("返回值:" + result);
}
@AfterThrowing(value = "servicePointcut()", throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("异常:" + e.getMessage());
}
}
七、环绕通知(重点⭐)
@Around("servicePointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
return result;
}
📌 注意:
- 必须调用
proceed() - 可以控制是否执行目标方法
八、通知执行顺序
正常执行
@Around
@Before
目标方法
@AfterReturning
@After
异常执行
@Around
@Before
目标方法
@AfterThrowing
@After
九、AOP + 自定义注解(企业常用)
1️⃣ 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
2️⃣ 切面使用
@Around("@annotation(log)")
public Object around(ProceedingJoinPoint pjp, Log log) throws Throwable {
System.out.println("操作:" + log.value());
return pjp.proceed();
}
3️⃣ 使用
@Log("新增用户")
public void addUser() {}
十、Spring AOP 的限制(高频面试)
❌ 同类方法内部调用,AOP 失效
public void a() {
b(); // 不会走 AOP
}
public void b() {}
原因:
👉 代理对象未生效
解决方案:
- 拆成不同类
- 通过
AopContext.currentProxy()
十一、AOP 常见应用场景
| 场景 | 示例 |
|---|---|
| 日志记录 | 操作日志、审计 |
| 事务管理 | @Transactional |
| 权限校验 | 登录、鉴权 |
| 性能统计 | 方法耗时 |
| 防重复提交 | 接口幂等 |
十二、Spring AOP vs AspectJ
| 对比 | Spring AOP | AspectJ |
|---|---|---|
| 实现方式 | 运行时代理 | 编译期 / 类加载期 |
| 粒度 | 方法级 | 方法、字段、构造器 |
| 性能 | 略低 | 更高 |
| 易用性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
一句话总结
Spring AOP 适合 90% 企业业务,简单高效