AOP中的五种通知类型在实际项目中如何选择?举例说明各自的典型应用场景

在Spring AOP中,五种通知类型各有其独特的执行时机和适用场景。选择正确的通知类型,可以让你的代码更加清晰、高效。为了让你快速建立整体认知,下面这个表格清晰地对比了它们的核心特性。

通知类型 核心执行时机 能否阻止目标方法执行 典型应用场景
@Around(环绕通知) 目标方法执行前后 可以 (不调用proceed()则方法不执行) 性能监控、事务管理、缓存、限流
@Before(前置通知) 目标方法执行 不能 (除非抛出异常) 权限校验、参数校验、日志记录
@AfterReturning(返回通知) 目标方法成功返回 不能 记录成功日志、对结果进行后处理(如脱敏)
@AfterThrowing(异常通知) 目标方法抛出异常 不能 记录错误日志、告警、异常统计
@After(后置通知) 目标方法执行(无论成败) 不能 资源清理、释放锁

💡 详解各类通知的应用场景

1. @Around:功能最强大的"总管"

这是最强大和灵活的通知,可以完全控制目标方法的执行。

  • 典型场景性能监控、声明式事务管理、缓存、限流
  • 代码示例:计算方法执行耗时。
java 复制代码
@Aspect
@Component
public class PerformanceAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            // 执行目标方法
            return pjp.proceed();
        } finally {
            long duration = System.currentTimeMillis() - start;
            String methodName = pjp.getSignature().getName();
            System.out.println("【性能监控】方法 " + methodName + " 执行耗时: " + duration + "ms");
        }
    }
}

2. @Before:尽职的"安全检查员"

在业务逻辑之前执行,适合做前置校验和记录。

  • 典型场景权限验证、参数校验、记录方法开始日志
  • 代码示例:简单的参数校验。
less 复制代码
@Aspect
@Component
public class ValidationAspect {
    @Before("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void validateParams(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg == null) {
                throw new IllegalArgumentException("请求参数不能为null");
            }
        }
    }
}

3. @AfterReturning:乐观的"成功处理员"

只在方法成功返回后执行,可以获取到方法的返回值。

  • 典型场景记录成功操作日志、对返回结果进行脱敏或格式化
  • 代码示例:对返回结果中的敏感信息进行脱敏。
less 复制代码
@Aspect
@Component
public class DataMaskingAspect {
    @AfterReturning(
        pointcut = "execution(* com.example.service.UserService.getUserInfo(..))",
        returning = "result"
    )
    public void maskSensitiveData(Object result) {
        if (result instanceof UserDTO) {
            UserDTO user = (UserDTO) result;
            // 对手机号进行脱敏
            String phone = user.getPhone();
            user.setPhone(phone.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2"));
        }
    }
}

4. @AfterThrowing:专业的"故障排查员"

只在目标方法抛出异常时执行,用于处理错误情况。

  • 典型场景记录详细的错误日志、发送告警通知、进行异常统计
  • 代码示例:异常日志记录与告警。
less 复制代码
@Aspect
@Component
public class ExceptionLogAspect {
    @AfterThrowing(
        pointcut = "execution(* com.example..*.*(..))",
        throwing = "ex"
    )
    public void logException(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().toShortString();
        // 1. 记录详细的错误日志,方便排查
        System.err.println("方法 [" + methodName + "] 执行异常: " + ex.getMessage());
        // 2. (可选) 集成告警系统,发送邮件或短信
        // alertService.sendAlert("系统异常告警", "异常方法: " + methodName, ex);
    }
}

5. @After:可靠的"后勤保障员"

无论方法是成功返回还是抛出异常,都会执行,类似于 try-catch-finally中的 finally块。

  • 典型场景释放资源(如文件流、数据库连接)、清理临时数据
  • 代码示例:资源清理。
less 复制代码
@Aspect
@Component
public class ResourceCleanupAspect {
    @After("execution(* com.example.service.FileService.process(..))")
    public void cleanupResources(JoinPoint joinPoint) {
        // 模拟释放资源,如关闭文件流、数据库连接等
        System.out.println("正在释放方法执行过程中占用的资源...");
        // resourceManager.cleanup(); // 实际的清理逻辑
    }
}

🎯 选择策略与最佳实践

  1. 遵循"最小权限"原则 :如果@Before就能满足需求(如参数校验),就不要用@Around。代码越简单,出错的概率越低。
  2. @Around是万能药,但亦有代价:它最强大,但也最复杂,使用不当可能导致目标方法未执行或异常被吞掉。务必在必要时(需要控制方法执行或处理异常)才使用它。
  3. 注意执行顺序 :当多个切面作用于同一方法时,可以使用@Order注解控制顺序。通常,像权限校验这类切面应具有最高优先级(@Order值最小),最先执行。
  4. 警惕"自调用"问题 :同一个类内部的方法调用,不会触发AOP代理。例如,在ServiceA的方法A中直接调用自己的方法B,方法B上的AOP通知是不会生效的。
相关推荐
JIngJaneIL18 分钟前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
小信啊啊41 分钟前
Go语言切片slice
开发语言·后端·golang
Victor3562 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易2 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧3 小时前
Range循环和切片
前端·后端·学习·golang
WizLC3 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor3563 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法3 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长3 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
Python编程学习圈4 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端