下面提供一份系统、可直接用于学习或面试的 Spring AOP 详解。内容覆盖核心概念、底层原理、常用注解、执行流程、实现方式以及实战示例,适合你直接应用到项目或文档中。
一、什么是 Spring AOP
**AOP(Aspect Oriented Programming,面向切面编程)**是对 OOP 的一种补充。
- OOP 关注的是业务对象(类与方法)。
- AOP 关注的是系统的横切逻辑(日志、事务、权限、监控等)。
AOP 能将这些“横切关注点”从业务逻辑中抽离出来,实现统一管理。
二、Spring AOP 的核心概念
| 概念 | 解释 |
|---|---|
| Aspect(切面) | 一个横切关注点(如日志切面、事务切面)。 |
| JoinPoint(连接点) | 代码中可插入切面逻辑的点(Spring 中是方法执行)。 |
| Pointcut(切点) | 决定在哪些方法上织入切面。 |
| Advice(通知) | 在切点位置执行的动作,如前置、后置、环绕等。 |
| Weaving(织入) | 将切面逻辑应用到目标对象的过程。 |
| Proxy(代理对象) | 织入切面后生成的代理对象,调用它会触发切面逻辑。 |
三、Spring AOP 的通知类型(Advice)
Spring AOP 提供五种通知:
- @Before
在目标方法执行前执行。 - @After
无论方法是否异常都会执行(类似 finally)。 - @AfterReturning
方法正常返回后执行。 - @AfterThrowing
方法抛出异常后执行。 - @Around
包裹整个方法执行,可手动控制方法执行时机、返回值、异常等。
是最强大的通知类型。
四、切点表达式(Pointcut Expression)
最常用语法:
execution(返回值 包路径.类名.方法名(参数))
示例:
@Pointcut("execution(* com.example.service.*.*(..))")
表示拦截 com.example.service 包下的所有类的所有方法。
其他常用示例:
| 描述 | 表达式 |
|---|---|
| 拦截某个类所有方法 | execution(* com.demo.UserService.*(..)) |
| 拦截所有 set 开头方法 | execution(* set*(..)) |
| 拦截某注解的方法 | @annotation(com.demo.Log) |
| 拦截某注解的类 | @within(com.demo.Log) |
五、Spring AOP 底层实现原理
Spring AOP 并不是靠字节码插桩,而是依靠动态代理技术。
情况一:如果目标类实现了接口
Spring 使用 JDK 动态代理
- Proxy.newProxyInstance()
- 代理对象和目标对象共享接口
情况二:目标类没有实现接口
Spring 使用 CGLIB 动态代理
- 基于 ASM 技术生成目标类的子类
- 重写目标方法插入切面逻辑
SpringBoot 默认使用 CGLIB(只要使用 @Configuration 等)。
六、AOP 执行流程(Spring IOC + AOP 整合)
- 容器启动时扫描切面(Aspect 类)
- 解析切点表达式并保存
- 使用 BeanPostProcessor(如
AnnotationAwareAspectJAutoProxyCreator)
在 Bean 初始化后判断是否需要代理 - 如果匹配切点 → 创建代理对象
- 调用方法时 → 进入代理逻辑 → 执行增强 → 调用目标方法 → 返回结果
AOP 的核心是:
容器返回的不是原始 Bean,而是代理 Bean。
七、实战示例:日志切面
1. 添加依赖(Spring Boot 已默认包含 AOP)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建切面类
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.demo.service.*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void before(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature().getName());
}
@AfterReturning(value = "serviceMethods()", returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
System.out.println("Return: " + result);
}
@AfterThrowing(value = "serviceMethods()", throwing = "ex")
public void afterThrowing(Exception ex) {
System.out.println("Exception: " + ex.getMessage());
}
@Around("serviceMethods()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Around before");
Object ret = pjp.proceed();
System.out.println("Around after");
return ret;
}
}
八、@Around 的核心增强逻辑
@Around("serviceMethods()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
// 执行目标方法
Object result = pjp.proceed();
System.out.println("耗时: " + (System.currentTimeMillis() - start));
return result;
} catch (Exception e) {
System.out.println("错误日志: " + e.getMessage());
throw e;
}
}
这是用 AOP 实现 统一日志、异常、耗时统计 的企业应用最常见写法。
九、Spring AOP vs AspectJ
| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 代理方式 | 动态代理(运行时) | 编译期/类加载期织入 |
| 性能 | 略慢 | 快 |
| 支持的 JoinPoint | 只有方法 | 方法、构造器、字段访问等 |
| 使用难度 | 简单 | 较高 |
| 场景 | 业务增强、日志、事务 | 深度 AOP、监控工具、SDK |
Spring AOP 足以满足大部分企业开发。
十、Spring AOP 最经典的应用场景
- 日志记录(请求日志、入参出参)
- 统一异常处理(拦截 Service 异常)
- 权限校验
- 接口限流
- 接口幂等性校验
- 事务管理(@Transactional 就是 AOP 实现)
- 性能监控 / 慢查询报警
如果你需要,我还可以进一步提供:
- AOP 事务底层原理解析(@Transactional 源码剖析)
- AOP 在 SpringBoot 项目中的最佳实践
- AOP 的常见 Bug 与坑(必看)
- AOP + 注解实现统一日志的完整可运行示例代码
告诉我你的下一步需求即可。
发表回复