开发者

一文详解Spring Aop @After(后置通知)的使用场景

开发者 https://www.devze.com 2025-06-28 12:44 出处:网络 作者: 冰糖心书房
目录核心定义@After 通知的执行流程@After 通知能做什么?(主要应用场景)与 @AfterReturning 和 @AfterThrowing 的区别代码示例总结核心定义
目录
  • 核心定义
  • @After 通知的执行流程
  • @After 通知能做什么?(主要应用场景)
  • 与 @AfterReturning 和 @AfterThrowing 的区别
    • 代码示例
  • 总结

    核心定义

    @After 是 Spring AOP 中的另一种通知(Advice)类型,通常被称为“后置通知”或“最终通知”。

    它的核心作用是:

    无论目标方法是正常执行完成,还是在执行过程中抛出了异常,@After 通知中的代码 总是 会在目标方法执行之后被执行。

    最经典的类比就是 Java 中的 try...catch...finally 语句块里的 finally 部分。@After 的行为和 finally 块的行为几乎一模一样。

    @After 通知的执行流程

    为了更好地理解,我们来看两种情况下的执行顺序:

    情况一:目标方法成功执行

    • @Before 通知执行。
    • 目标方法 (targetMethod()) 执行并正常返回。
    • @After 通知执行。
    • (如果定义了)@AfterReturning 通知执行。

    情况二:目标方法抛出异常

    • @Before 通知执行。
    • 目标方法 (targetMethod()) 执行,中途抛出异常。
    • @After 通知执行。
    • (如果定义了)@AfterThrowing 通知执行。
    • 异常继续向上层调用栈抛出。

    一个非常关键的点是:@After 通知本身无法访问目标方法的返回值(因为它可能根本没有返回值,比如抛异常时),也无法捕获或处理从目标方js法中抛出的异常。它只是一个保证“最后一定会被执行”的钩子。

    @After 通知能做什么?(主要应用场景)

    后置通知非常适合执行那些必须进行的“清理”或“收尾”工作,无论业务逻辑成功与否。

    资源释放 (Resource Cleanup)

    • 这是 @After 最重要、最常见的用途。类似于 finally 块。
    • 示例:释放文件句柄、关闭网络连接、关闭数据库连接池中的连接等。确保即使业务代码出错,关键资源也不会被泄露。

    上下文清理 (Context Cleanup)

    • 如果在 @Before 通知中向 ThreadLocal 存放了数据,那么在 @After 通知中将其 remove() 是一个最佳实践。这可以防止在线程池环境中发生内存泄漏或数据错乱。

    最终日志记录 (Final Auditing)

    • 记录一个操作的结束。
    • 示例:“方法 updateProduct 执行完毕。” 这个日志不关心成功或失败,只记录“结束”这个事实。

    性能监控 (Performance Monitoring)

    • 可以在 @Before 中记录一个开始时间,然后在 @After 中记录结束时间,并计算总耗时。

    示例:

    • @Before: long startTime = System.currentTimeMillis(); (存入 ThreadLocal)
    • @After: long endTime = System.currentTimeMillis(); long duration = endTime - startTime; log.info("方法耗时: {} ms", duration);

    与 @AfterReturning 和 @AfterThrowing 的区别

    这是新手很容易混淆的地方,理解它们的区别至关重要:

    知类型执行时机能否访问返回值?能否访问异常?主要用途
    @After (最终通知)总是在目标方法后执行(无论成功或失败)不能不能资源清理、最终日志
    @AfterReturning (返回通知)仅在目标方法成功执行后执行可以不适用基于返回结果的附加操作
    @AfterThrowing (异常通知)仅在目标方法抛出异常后执行不适用可以异常日志记录、告警通知

    简单来说:

    • 总是执行清理?用 @After
    • 想在成功后根据返回值做点事?用 @AfterReturning
    • 想在失败后专门处理异常?用 @AfterThrowing

    代码示例

    我们扩展之前的例子,增加一个删除方法(可能会失败),并为所有方法添加 @After 通知。

    1. 业务服务类 (目标对象)

    package com.example.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
    
        // 成功执行的例子
        public String findUserById(Long id) {
            System.out.println("--- 核心业务逻辑:正在根据 ID 查询用户... ---");
            return "User" + id;
        }
    
        // 抛出异常的例子
        public void deleteUser(Long id) {
            System.out.println("--- 核心业务逻辑:正在尝试删除用户... ---");
            if (id <= 0) {
                throw new IllegalArgumentException("用户ID无效,删除失败!");
            }
            System.out.println("用户 " + id + " 已被成功删除。");
        }
    }
    

    2. 切面类 (ASPect) 中定义编程客栈 @After 通知

    package com.example.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.pythonannotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    import java.util.Arrays;
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        @Pointcut("executwww.devze.comion(public * com.example.service.*.*(..))")
        public void serviceLayerPointcut() {}
    
        // 前置通知
        @Before("serviceLayerPointcut()")
        public void logBefore(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            System.out.println("==================================================");
            System.out.printf("[AOP 前置通知]: 方法 [%s] 即将执行... 参数: %s%n", methodName, Arrays.toString(args));
        }
    
        // 后置通知 (最终通知)
        @After("serviceLayerPointcut()")
        public void logAfter(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            System.out.printf("[AOP 后置通知]: 方法 [%s] 执行完毕。执行清理工作...%n", methodName);
            System.out.println("--------------------------------------------------\n");
        }
    }
    

    3. 运行代码并观察输出

    调用成功的方法 userService.findUserById(101L):

    ==================================================
    [AOP 前置通知]: 方法 [findUserById] 即将执行... 参数: [101]
    --- 核心业务逻辑:正在根据 ID 查询用户... ---
    [AOP 后置通知]: 方法 [findUserById] 执行完毕。执行清理工作...
    --------------------------------------------------
    

    @After 在方法成功后执行了。

    调用失败的方法 userService.deleteUser(0L) (需要用 try-catch 捕获异常):

    try {
        userService.deleteUser(0L);
    } catch (Exception e) {
        System.err.println("在调用方捕获到异常: " + e.getMessage());
    }
    

    输出:

    =============================================www.devze.com=====
    [AOP 前置通知]: 方法 [deleteUser] 即将执行... 参数: [0]
    --- 核心业务逻辑:正在尝试删除用户... ---
    [AOP 后置通知]: 方法 [deleteUser] 执行完毕。执行清理工作...
    --------------------------------------------------
    
    在调用方捕获到异常: 用户ID无效,删除失败!
    

    即使 deleteUser 抛出了异常,@After 通知 (logAfter 方法) 依然被执行了,完美地展示了其 finally 的特性。

    总结

    特性描述
    执行时机无论成功或失败,总是在目标方法执行之后执行。
    核心用途资源释放上下文清理、最终日志记录等收尾工作。
    行为类似Java 的 finally 语句块。
    关键限制无法访问目标方法的返回值,也无法捕获或修改异常。
    关键参数可以注入 JoinPoint 对象,获取方法元数据。

    以上就是一文详解Spring Aop @After(后置通知)的使用场景的详细内容,更多关于Spring Aop @After使用场景的资料请关注编程客栈(www.devze.com)其它相关文章!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号