Spring AOP @AfterReturning (返回通知)的使用场景

在这里插入图片描述

核心定义

@AfterReturning 是 Spring AOP 中一个非常重要的通知(Advice类型。它的核心作用是:

仅在目标方法(连接点)成功执行正常返回之后,执行一段特定的逻辑。如果目标方法在执行期间抛出异常,则该通知不会被执行。

它就像是为一次成功的任务举行的“庆功会”或“成果验收”。只有当任务圆满完成(方法正常返回),这个后续步骤才会被触发。


@AfterReturning 的关键特性

  1. 执行时机:只在目标方法成功返回后执行。
  2. 访问返回值可以! 这是它与 @After (后置通知) 最核心的区别。@AfterReturning 的设计目的之一就是让我们能够获取并处理目标方法的返回值。
  3. 修改返回值不能! 这是一个非常重要的点。@AfterReturning 通知是在返回值已经生成并准备交还给调用者之后执行的,所以你可以在通知里读取它、记录它、或者基于它执行其他操作,但你无法直接修改这个返回值来影响最终的调用结果。如果需要修改返回值,必须使用功能更强大的 @Around (环绕通知)。

@AfterReturning 能做什么?(主要应用场景)

既然可以获取返回值,它的应用场景就非常明确了,主要集中在对“结果”的后处理上。

  1. 对返回结果进行日志记录

    • 这是最直接的应用。记录方法成功执行后返回了什么内容,对于调试和审计非常有用。
    • 示例:“方法 findUserById(101) 成功执行,返回值是 User{id=101, name='Alice'}”。
  2. 对返回结果进行二次处理或触发后续操作

    • 根据返回的结果,执行一些非侵入性的附加业务逻辑。
    • 示例:当一个“注册用户”的方法成功返回一个新的 User 对象后,@AfterReturning 通知可以获取到这个 User 对象,然后调用消息队列服务,发送一封欢迎邮件。这个“发送邮件”的逻辑与“注册用户”的核心业务解耦,非常清晰。
  3. 数据缓存

    • 当一个查询方法成功返回数据后,可以将这个返回结果存入缓存(如 Redis、Caffeine)。
    • 示例getProductById(id) 成功返回 Product 对象后,@AfterReturning 通知将这个 Product 对象以 id 为键存入缓存。
  4. 数据转换或格式化(用于日志或监控,而非修改)

    • 获取到返回值后,可以将其转换为特定的格式(如 JSON)再进行记录。

如何获取返回值?

要获取返回值,你需要在 @AfterReturning 注解中使用 returning 属性。

  • returning 属性的值是一个字符串,它指定了通知方法中哪个参数用来接收返回值。
  • 这个参数名必须与通知方法签名中的一个参数名完全匹配。

语法:

@AfterReturning(pointcut = "yourPointcut()", returning = "result")
public void myAdviceMethod(JoinPoint joinPoint, Object result) {
    // 'result' 参数就会接收到目标方法的返回值
    // 参数类型最好是 Object,以增加通用性
}

代码示例

让我们用一个例子来演示如何获取并处理返回值。

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

package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class ProductService {

    // 成功返回一个字符串
    public String getProductById(long id) {
        System.out.println("--- 核心业务逻辑:正在查询产品 " + id + " ---");
        return "Product-" + id;
    }

    // 抛出异常的方法
    public void deleteProduct(long id) {
        System.out.println("--- 核心业务逻辑:正在删除产品 " + id + " ---");
        if (id <= 0) {
            throw new RuntimeException("产品ID无效,删除失败!");
        }
        System.out.println("产品 " + id + " 删除成功。");
    }
}

2. 切面类 (Aspect) 中定义 @AfterReturning 通知

package com.example.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ResultLoggingAspect {

    // 拦截 ProductService 中的所有公共方法
    @Pointcut("execution(public * com.example.service.ProductService.*(..))")
    public void productServicePointcut() {}

    /**
     * 定义返回通知
     * 1. 使用 @AfterReturning 注解
     * 2. 指定切点 "productServicePointcut()"
     * 3. 使用 returning = "returnValue" 来指定接收返回值的参数名
     */
    @AfterReturning(pointcut = "productServicePointcut()", returning = "returnValue")
    public void logResult(JoinPoint joinPoint, Object returnValue) {
        String methodName = joinPoint.getSignature().getName();
        
        System.out.println("==================================================");
        System.out.printf("[AOP 返回通知]: 方法 [%s] 成功执行完成。%n", methodName);

        // 检查返回值类型,并进行处理
        if (returnValue != null) {
            System.out.printf("[AOP 返回通知]: 返回值为: [%s], 返回值类型为: %s%n",
                    returnValue, returnValue.getClass().getName());
            // 在这里可以做更多事情,比如将 returnValue 存入缓存
        } else {
            // 对于 void 方法,returnValue 为 null
             System.out.println("[AOP 返回通知]: 方法没有返回值 (void)。");
        }
        System.out.println("==================================================\n");
    }
}

3. 运行代码并观察输出

调用成功返回数据的方法 productService.getProductById(888L):

--- 核心业务逻辑:正在查询产品 888 ---
==================================================
[AOP 返回通知]: 方法 [getProductById] 成功执行完成。
[AOP 返回通知]: 返回值为: [Product-888], 返回值类型为: java.lang.String
==================================================

@AfterReturning 被触发,并成功获取到了返回值 "Product-888"

调用抛出异常的方法 productService.deleteProduct(-1L):

try {
    productService.deleteProduct(-1L);
} catch (Exception e) {
    System.err.println("调用方捕获到异常: " + e.getMessage());
}

输出:

--- 核心业务逻辑:正在删除产品 -1 ---
调用方捕获到异常: 产品ID无效,删除失败!

观察输出,你会发现没有任何关于 [AOP 返回通知] 的日志。这证明了当方法抛出异常时,@AfterReturning 通知是不会被执行的。

总结

特性描述
执行时机仅在目标方法成功执行并正常返回后。
核心用途对方法的返回结果进行后续处理,如日志记录、结果缓存、触发异步任务。
能否访问返回值可以,通过 returning 属性指定接收参数。
能否修改返回值不可以。如需修改,请使用 @Around 通知。
@After 的区别@After 总是执行(像 finally),而 @AfterReturning 只在成功时执行;@After 无法获取返回值,而 @AfterReturning 可以。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值