Spring AOP(二)Spring AOP 完整指南
- Spring AOP 完整指南
- 一、`@Around` 切入点表达式大全
- 二、`execution` 表达式详解(最强大)
- 三、组合表达式
- 四、Spring AOP 五种通知类型
- 五、各通知类型代码示例
- 1. `@Around` - 环绕通知(最强大)
- 2. `@Before` - 前置通知
- 3. `@AfterReturning` - 后置返回通知
- 4. `@AfterThrowing` - 异常通知
- 5. `@After` - 最终通知
- 六、执行顺序对比
- 七、实战案例:完整的切面
- 八、选择建议
- 九、注意事项
Spring AOP 完整指南
一、@Around切入点表达式大全
常用切入点表达式表格
| 表达式 | 说明 | 示例 |
|---|
@annotation(注解名) | 匹配标注了某个注解的方法 | @annotation(authCheck) |
execution(...) | 匹配方法执行(最常用) | execution(* com.example.service.*.*(..)) |
within(包或类) | 匹配指定包或类中的所有方法 | within(com.example.service.*) |
this(类型) | 匹配代理对象是指定类型的方法 | this(com.example.service.UserService) |
target(类型) | 匹配目标对象是指定类型的方法 | target(com.example.service.UserService) |
args(参数类型) | 匹配方法参数符合类型的方法 | args(java.lang.String, ..) |
@within(注解) | 匹配标注了某个注解的类中的所有方法 | @within(org.springframework.stereotype.Service) |
@target(注解) | 匹配目标对象标注了某个注解的方法 | @target(org.springframework.stereotype.Controller) |
@args(注解) | 匹配方法参数标注了某个注解的方法 | @args(com.example.annotation.Sensitive) |
bean(bean名称) | 匹配指定名称的 Bean | bean(userService) |
二、execution表达式详解(最强大)
语法格式
execution(修饰符? 返回类型 方法名(参数) throws 异常?)
常用示例表格
| 表达式 | 说明 |
|---|
execution(public * *(..)) | 所有 public 方法 |
execution(* set*(..)) | 所有以 “set” 开头的方法 |
execution(* com.example.service.*.*(..)) | service 包下所有类的所有方法 |
execution(* com.example.service..*.*(..)) | service 包及其子包下所有方法 |
execution(* com.example.service.UserService.*(..)) | UserService 类的所有方法 |
execution(public User com.example.service.UserService.getById(..)) | 精确匹配 getById 方法 |
execution(* *(java.lang.String)) | 只有一个 String 参数的方法 |
execution(* *(.., java.lang.String)) | 最后一个参数是 String 的方法 |
execution(* *(java.util.List, ..)) | 第一个参数是 List 的方法 |
execution(* com.example.service.*.*(..)) && !execution(* *.delete*(..)) | service 包所有方法,排除 delete 开头 |
通配符说明
| 通配符 | 含义 |
|---|
* | 匹配任意字符(一个) |
.. | 匹配多个包、多个参数 |
+ | 匹配子类 |
三、组合表达式
| 运算符 | 说明 | 示例 |
|---|
&& | 与(同时满足) | execution(* com.example.service.*.*(..)) && @annotation(authCheck) |
|| | 或(满足其一) | @annotation(authCheck) || @annotation(loginCheck) |
! | 非(不满足) | !execution(* *.delete*(..)) |
四、Spring AOP 五种通知类型
完整对比表格
| 通知类型 | 注解 | 执行时机 | 能否控制方法执行 | 典型应用场景 |
|---|
| 环绕通知 | @Around | 方法执行前后 | ✅ 能(通过proceed()) | 权限校验、事务管理、性能监控 |
| 前置通知 | @Before | 方法执行前 | ❌ 不能 | 日志记录、参数校验 |
| 后置通知 | @AfterReturning | 方法成功返回后 | ❌ 不能 | 处理返回值、缓存更新 |
| 异常通知 | @AfterThrowing | 方法抛出异常后 | ❌ 不能 | 异常处理、告警通知 |
| 最终通知 | @After | 方法执行完成后(无论成功或失败) | ❌ 不能 | 资源释放、清理工作 |
五、各通知类型代码示例
1.@Around- 环绕通知(最强大)
@Around("@annotation(authCheck)")publicObjectdoAround(ProceedingJoinPointjoinPoint,AuthCheckauthCheck)throwsThrowable{System.out.println("=== 前置处理 ===");// 执行原方法Objectresult=joinPoint.proceed();System.out.println("=== 后置处理 ===");returnresult;}
2.@Before- 前置通知
@Before("@annotation(authCheck)")publicvoiddoBefore(JoinPointjoinPoint,AuthCheckauthCheck){// 获取方法名StringmethodName=joinPoint.getSignature().getName();System.out.println("即将执行方法: "+methodName);// 获取参数Object[]args=joinPoint.getArgs();System.out.println("参数: "+Arrays.toString(args));}
3.@AfterReturning- 后置返回通知
@AfterReturning(value="@annotation(authCheck)",returning="result")publicvoiddoAfterReturning(JoinPointjoinPoint,Objectresult){System.out.println("方法执行成功,返回值: "+result);// 可以修改返回值(但不推荐)if(resultinstanceofBaseResponse){((BaseResponse<?>)result).setMessage("操作成功");}}
4.@AfterThrowing- 异常通知
@AfterThrowing(value="@annotation(authCheck)",throwing="exception")publicvoiddoAfterThrowing(JoinPointjoinPoint,Throwableexception){StringmethodName=joinPoint.getSignature().getName();System.err.println("方法 "+methodName+" 执行失败: "+exception.getMessage());// 记录错误日志、发送告警等log.error("方法 {} 执行异常",methodName,exception);}
5.@After- 最终通知
@After("@annotation(authCheck)")publicvoiddoAfter(JoinPointjoinPoint){// 无论成功还是失败都会执行System.out.println("方法执行完成,进行资源清理");}
六、执行顺序对比
正常执行流程
@Around 前置处理 ↓ @Before 前置通知 ↓ 执行原方法 ↓ @AfterReturning 后置返回通知 ↓ @After 最终通知 ↓ @Around 后置处理
异常执行流程
@Around 前置处理 ↓ @Before 前置通知 ↓ 执行原方法(抛出异常) ↓ @AfterThrowing 异常通知 ↓ @After 最终通知 ↓ @Around 捕获异常(可选)
七、实战案例:完整的切面
@Aspect@Component@Slf4jpublicclassLogAspect{@Around("@annotation(logAnnotation)")publicObjectaround(ProceedingJoinPointjoinPoint,LoglogAnnotation)throwsThrowable{longstartTime=System.currentTimeMillis();try{// @Before 的逻辑log.info("开始执行方法: {}",joinPoint.getSignature().getName());// 执行原方法Objectresult=joinPoint.proceed();// @AfterReturning 的逻辑longendTime=System.currentTimeMillis();log.info("方法执行成功,耗时: {}ms",endTime-startTime);returnresult;}catch(Throwablee){// @AfterThrowing 的逻辑log.error("方法执行失败: {}",e.getMessage(),e);throwe;}finally{// @After 的逻辑log.info("方法执行完成");}}}
八、选择建议
| 场景 | 推荐通知 |
|---|
| 需要控制方法是否执行(如权限校验) | @Around |
| 需要修改返回值 | @Around |
| 只需要在方法前做处理(如日志) | @Before |
| 只需要处理方法返回值 | @AfterReturning |
| 只需要处理异常情况 | @AfterThrowing |
| 需要释放资源(无论成功失败) | @After |
| 性能监控(计算耗时) | @Around |
九、注意事项
@Around最强大,可以替代其他所有通知,但不要滥用@Before无法阻止方法执行,只能在前做处理@AfterReturning只在成功时执行,异常时不执行@AfterThrowing只在异常时执行,成功时不执行@After无论如何都会执行,类似finally- 多个切面的执行顺序可以使用
@Order注解控制 - 切入点表达式越精确越好,避免不必要的拦截
总结:
execution是最强大的切入点表达式,几乎可以匹配任何方法@Around是最灵活的通知类型,可以完全控制方法的执行- 实际开发中,
@Around+execution/@annotation组合使用最为常见