SpringBoot 使用【AOP 切面+注解】实现在请求调用 Controller 方法前修改请求参数和在结果返回之前修改返回结果

前情提要

在项目中需要实现 在请求调用 Controller 方法前修改请求参数和在结果返回之前修改返回结果

我们可以使用 AOP 切面+注解的形式实现。这样我们就可以在不修改原始代码的情况下,通过切面类在方法调用前后插入额外的逻辑。

解决方案

自定义注解 @PreProcess

自定义注解 @PreProcess 用于标记需要在方法调用前进行预处理的方法,以便后续处理。

java 复制代码
@Target(ElementType.METHOD) // 表示该注解只能应用在方法上
@Retention(RetentionPolicy.RUNTIME) // 表示该注解在运行时仍然可用
public @interface PreProcess {
    // 可以添加其他注解属性的注释
}

自定义切面类 ModifiyAspect

接下来我们需要创建一个切面类,用于实现对带有自定义 @PreProcess 注解的方法进行处理。切面类代码如下:

java 复制代码
/**
 * 用于对带有 @PreProcess 注解的方法进行处理的切面类。
 */
@Aspect // 表示该类是切面类
@Component // 表示该类为 Spring 组件
@Order(1001) // 表示该切面类的优先级为 1001
public class ModifiyAspect {

    /**
     * 定义切入点,用于匹配带有 @PreProcess 注解的方法。
     */
    @Pointcut("@annotation(com.example.demo.demos.annotation.PreProcess)")
    public void preProcess() {
    }

    /**
     * 在方法调用后进行处理,修改返回结果。
     * @param joinPoint 连接点对象
     * @return 修改后的返回结果
     * @throws Throwable 异常
     */
    @Around("preProcess()")
    public Object modifyResult(ProceedingJoinPoint joinPoint) throws Throwable {

        // 1.获取方法返回结果
        Object result = joinPoint.proceed();

        // 2.修改返回结果
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(result));
        jsonObject.put("aspect", "aspect");
        
        // 3.返回修改后的结果
        result = jsonObject;
        return result;
    }

    /**
     * 在方法调用前进行处理,修改请求参数。
     * @param joinPoint 连接点对象
     * @return 修改后的返回结果
     * @throws Throwable 异常
     */
    @Around("preProcess()")
    public Object modifyParam(ProceedingJoinPoint joinPoint) throws Throwable {

        // 1.获取请求参数
        Object[] args = joinPoint.getArgs();
        Object arg = args[0];
        
        // 2.修改请求参数
        JSONObject jsonData = JSONObject.parseObject(JSON.toJSONString(arg));
        jsonData.put("test", "test");

        // 3.返回修改后的请求参数
        args[0] = JSONObject.parse(JSON.toJSONString(jsonData));
        return  joinPoint.proceed(args);
    }

}

在这个切面类中,通过对方法调用前后的处理,实现了对返回结果和请求参数的修改。

该类具体包含以下几个部分:

  1. 类注解:

    • @Aspect:表示该类是一个切面类,用于声明该类中包含了切面逻辑。
    • @Component:表示该类是 Spring 组件,用于让 Spring 容器自动扫描和管理该类。
    • @Order:表示该切面类的优先级,用于指定组件的加载顺序或切面的执行顺序,优先级高的切面类将先被执行,数值越小,优先级越高,默认值为 Integer.MAX_VALUE
  2. 方法注解:

    • @Pointcut:定义切入点,即匹配带有 @PreProcess 注解的方法。
    • @Around:表示该方法是一个环绕通知,在方法调用前后都会执行。
  3. 方法:

    • modifyResult() 方法在方法调用后,将返回结果转换为 JSON 对象,并添加一个名为 "aspect" 的属性,表示该返回结果是由该切面类修改的。

    • modifParam() 方法在方法调用前,将传入的请求参数转换为 JSON 对象,并添加一个名为 "test" 的属性,表示该请求参数是由该切面类修改的。

注:

joinPoint.getArgs() 方法返回一个 Object[] 数组,其中包含了方法调用时传入的所有参数。

这里假设方法调用时至少传入了一个参数,并且只取第一个参数进行处理。因此通过 args[0] 可以获取到第一个参数(索引为0)。

如果方法调用时没有传入参数,或者需要处理多个参数,可以根据实际情况进行修改。

编写 Controller 测试

创建两个接口,同样的逻辑,接收一个请求体参数 params,再将接收的参数以 JSON 格式返回:

java 复制代码
@RestController
public class BasicController {

    /**
     * 处理 /hello 请求的方法
     * @param params 请求体参数,以键值对的形式传递
     * @return 经过转换后的 JSONObject 对象
     */
    @PreProcess
    @PostMapping("/hello")
    public JSONObject hello(@RequestBody Map<String, Object> params) {
        return JSONObject.parseObject(JSON.toJSONString(params));
    }

    @PostMapping("/hello1")
    public JSONObject hello1(@RequestBody Map<String,Object> params) {
        return JSONObject.parseObject(JSON.toJSONString(params));
    }
}

启动项目,在 ApiFox 中分别以同样的请求参数发送 POST 请求调用 /hello/hello1 接口:

  • 请求参数:

    json 复制代码
    {
        "name": "hello",
        "age": 20
    }
  • /hello 接口返回结果:

    json 复制代码
    {
        "test": "test",
        "aspect": "aspect",
        "name": "hello",
        "age": 20
    }
  • /hello1 接口返回结果:

    json 复制代码
    {
        "name": "hello",
        "age": 20
    }

复制多个 ModifiyAspect 切面模拟多切面同时修改请求体参数和返回结果,使用@Order注解指定执行顺序,测试结果如下:

json 复制代码
{
    "test2": "test2",	//切面二
    "test": "test",	//切面一
    "aspect": "aspect",	//切面一
    "name": "hello",
    "aspect2": "aspect2",	//切面二
    "age": 20
}
相关推荐
yyt3630458416 分钟前
spring单例bean线程安全问题讨论
java·spring
Sweet锦17 分钟前
SpringBoot 3.5 集成 InfluxDB 1.8
spring boot·时序数据库
我是大猴子27 分钟前
事务失效的几种情况以及是为什么(详解)
java·开发语言
wertyuytrewm1 小时前
Java面试——Java基础
java·jvm·面试
Java水解1 小时前
Spring Boot 消息队列与异步处理
spring boot·后端
czlczl200209251 小时前
RAG实现思路流程
java·jvm
带娃的IT创业者1 小时前
WeClaw_40_系统监控与日志体系:多层次日志架构与Trace追踪
java·开发语言·python·架构·系统监控·日志系统·链路追踪
Y001112361 小时前
JDBC原理
java·开发语言·数据库·jdbc
程序员侠客行2 小时前
Tomcat 从陌生到熟悉
java·tomcat·web
wertyuytrewm2 小时前
Java 异常|Java Exceptions
java·开发语言