Spring Boot AOP(一) 入门与核心概念
1. AOP 基础概念
AOP(Aspect-Oriented Programming,[面向切面编程])是一种用于处理 横切关注点(Cross-Cutting Concerns)的[编程技术]。在企业级项目中,日志记录、性能监控、事务管理、权限校验等通常会重复出现在多个模块,如果将这些逻辑直接写入业务方法,会导致代码耦合高、难维护。AOP 通过 切面(Aspect) 将这些横切逻辑模块化,从而解耦业务逻辑。
核心概念
| 概念 | 说明 | 示例 |
|---|---|---|
| 切面 Aspect | 横切关注点模块化封装 | 日志切面、事务切面 |
| 通知 Advice | 切面中具体执行操作 | @Before、@After、@Around |
| 连接点 JoinPoint | 可以被切面切入的位置 | 方法调用、异常抛出 |
| 切入点 Pointcut | 匹配连接点的表达式 | execution(* com.example.service… . (…)) |
| 织入 Weaving | 将切面应用到目标对象的过程 | Spring AOP 在运行时生成代理 |
Spring AOP 默认使用 运行时动态代理,只对 Spring 管理的 Bean 生效,无法处理普通对象的直接方法调用。
2. 连接点与切入点表达式
2.1 JoinPoint 常用方法
@Around("execution(* com.example.service..*.*(..))")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("目标对象: " + pjp.getTarget());
System.out.println("方法签名: " + pjp.getSignature());
System.out.println("方法参数: " + Arrays.toString(pjp.getArgs()));
return pjp.proceed();
}
AI写代码java
运行
1234567
| 方法 | 说明 |
|---|---|
| getTarget() | 目标对象 |
| getThis() | 当前代理对象 |
| getArgs() | 方法参数 |
| getSignature() | 方法签名 |
| proceed() | 执行目标方法(环绕通知专用) |
2.2 切入点表达式常用类型
| 表达式 | 含义 | 示例 |
|---|---|---|
| execution() | 匹配方法执行 | execution(* com.example.service… . (…)) |
| within() | 匹配类或包 | within(com.example.service…*) |
| this() | 匹配代理对象类型 | this(com.example.service.MyService) |
| target() | 匹配目标对象类型 | target(com.example.service.MyService) |
| args() | 匹配参数类型 | args(String, …) |
2.3 切入点示意图
匹配
不匹配
Service 层方法
匹配切入点?
执行切面通知
直接执行目标方法
3. Spring AOP 通知类型
| 类型 | 执行时机 | 注解 | 适用场景 |
|---|---|---|---|
| 前置通知 | 方法执行前 | @Before | 权限校验、日志记录 |
| 后置通知 | 方法执行后 | @After | 日志记录、资源清理 |
| 返回通知 | 方法成功返回后 | @AfterReturning | 日志记录、返回值处理 |
| 异常通知 | 方法抛出异常后 | @AfterThrowing | 异常记录、告警 |
| 环绕通知 | 方法执行前后 | @Around | 性能统计、异常统一处理 |
通知执行顺序示意
AI写代码mermaid
12345678
4. 简单切面示例
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.service..*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("前置通知: 调用方法 " + joinPoint.getSignature().getName());
}
@AfterReturning(pointcut = "execution(* com.example.service..*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("返回通知: 方法返回值 " + result);
}
@Around("execution(* com.example.service..*.*(..))")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("环绕通知: 方法执行前");
Object result = pjp.proceed();
System.out.println("环绕通知: 方法执行后, 耗时 " + (System.currentTimeMillis() - start) + "ms");
return result;
}
}
AI写代码java
运行
1234567891011121314151617181920212223
5. 方法调用流程示意
ClientProxyTarget调用方法执行 @Before调用目标方法返回结果执行 @AfterReturning 或 @AfterThrowing返回最终结果ClientProxyTarget
6. 环绕通知深入解析
环绕通知 (@Around) 可以完全控制目标方法的执行:
- 可以修改参数
- 可以捕获异常
- 可以修改返回值
- 可以决定是否执行目标方法
@Around("execution(* com.example.service..*.*(..))")
public Object secureAround(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
// 修改参数
args[0] = "modified";
try {
Object result = pjp.proceed(args);
return result;
} catch (Throwable ex) {
System.out.println("捕获异常: " + ex.getMessage());
throw ex;
}
}
AI写代码java
运行
12345678910111213
流程图:环绕通知控制流程
AI写代码mermaid
12345678
7. AOP 与 Bean 生命周期交互
Spring AOP 使用 BeanPostProcessor 在 Bean 初始化后生成代理:
是
否
BeanDefinition 注册
Bean 实例化
依赖注入
postProcessBeforeInitialization
初始化方法
postProcessAfterInitialization
需要代理?
创建代理对象
直接返回 Bean
代理对象注入到容器
核心类:
AnnotationAwareAspectJAutoProxyCreatorProxyFactory/EnhancerAdvisor/Advice/Pointcut
8. 实战案例:日志 + 性能切面
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service..*.*(..))")
public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long duration = System.currentTimeMillis() - start;
System.out.println(pjp.getSignature() + " 耗时: " + duration + "ms");
return result;
}
}
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service..*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("调用方法: " + joinPoint.getSignature().getName());
}
@AfterReturning(pointcut = "execution(* com.example.service..*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("方法返回值: " + result);
}
}
AI写代码java
运行
12345678910111213141516171819202122232425262728
多切面调用顺序示意
AI写代码mermaid
1234567
9. 本文小结
- AOP 是处理横切关注点的强大机制
- 切面 + 通知 + 切入点构成核心
- 环绕通知最灵活,可控制方法执行前后
- 多切面、Bean 生命周期、通知组合都需要清楚理解
- Mermaid 图帮助理解调用顺序和执行流程