您的位置:

深入理解Spring AOP中的afterreturning

Spring框架中的AOP技术(面向切面编程)是一种可以在不修改源代码的情况下,对系统进行横向切割的技术。其中,afterreturning是其中一个重要的切面类型,它允许在方法正常返回之后,在方法返回上下文中引入一个切面。下面从多个角度深入理解afterreturning中的细节和用法。

一、用法概述

正如其名,afterreturning是在方法正常返回之后执行的增强类型。它拥有三个最常用的属性:

  • value:指定切点表达式
  • returning:指定一个用于接受方法返回值的参数名
  • argNames:指定方法参数名,逗号分隔

下面是一个afterreturning的示例:

@Aspect
@Component
public class LogAspect {
    
    @AfterReturning(value = "execution(* com.example.service.UserService.*(..))", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        System.out.println("AfterReturning: " + className + " " + methodName + " returning " + result);
    }
}

以上代码实现了在UserService中方法正常返回之后输出方法名称和返回结果的功能。这里需要注意的是,returning属性指定的参数名称必须要与方法签名中的参数名完全一致,否则无法正常接收返回值。

二、如何获取方法参数

有时候我们需要获取方法的各个参数来进一步处理,对于这个需求,afterreturning也可以支持。需要借助两个注解:@AfterReturning和@AfterReturning arg。示例代码如下:

@AfterReturning(value = "execution(* com.example.service.UserService.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
    String methodName = joinPoint.getSignature().getName();
    String className = joinPoint.getTarget().getClass().getSimpleName();
    System.out.println("AfterReturning: " + className + " " + methodName + " returning " + result);
    Object[] args = joinPoint.getArgs();
    for (Object obj : args) {
        if (obj instanceof HttpServletRequest) {
            System.out.println("HttpServletRequest: " + obj);
        }
    }
}

@AfterReturning(argNames = "request,result", value = "execution(* com.example.controller.UserController.getAll(..))")
public void logAfterReturning(JoinPoint joinPoint, HttpServletRequest request, Object result) {
    String methodName = joinPoint.getSignature().getName();
    String className = joinPoint.getTarget().getClass().getSimpleName();
    System.out.println("AfterReturning: " + className + " " + methodName + " returning " + result);
}

以上代码演示了如何获取方法中的HttpServletRequest参数。需要注意的是,在后面的示例代码中,argNames属性指定了方法签名中的参数名,这样我们就能够通过参数名将参数一一对应起来,方便我们在增强中处理它们。

三、afterreturning的用途

在实际应用中,afterreturning有很多种用途。下面我们列出了三种最常见的:

1、日志输出

以前面的日志输出示例为例,afterreturning是用来实现日志输出功能的。在方法正常返回之后,通过增强完成打印日志的功能。

2、返回值处理

有时候,我们需要对方法返回值进行一些处理。例如,添加一些统一的前缀或后缀等等。下面是一个示例代码:

@AfterReturning(value = "execution(* com.example.service.UserService.*(..))", returning = "result")
public void addPrefixToResult(JoinPoint joinPoint, Object result) {
    String className = joinPoint.getTarget().getClass().getSimpleName();
    if (result instanceof String) {
        result = "[Prefix]" + result + "[Suffix]";
    }
    System.out.println("AfterReturning: " + className + " returning " + result);
}

以上代码实现了给返回的String类型结果添加Prefix和Suffix的功能。需要注意的是,result是一个Object类型,需要判断其是否为String类型,然后再进行增强操作。

3、缓存实现

通过缓存实现数据的快速读取是很多高并发场景下的解决方案。在Spring中,afterreturning可以用于缓存实现,示例代码如下:

@AfterReturning(value = "execution(* com.example.service.UserService.getUserById(String)) && args(id)", returning = "result")
public void cacheUserById(String id, UserDTO userDTO) {
    redisTemplate.opsForValue().set(id, userDTO);
}

以上代码实现了通过ID缓存UserDTO的功能,在方法正常返回之后将其缓存到Redis中,下次查询相同的ID时可以避免数据库查询的开销。

四、总结

本文从用法概述、如何获取方法参数、afterreturning的用途三个方面深入理解了afterreturning的细节和用法。通过灵活使用这个切面类型,我们可以避免在修改源代码的情况下进行代码横向切割和重构。