前情提要
在项目中需要实现 在请求调用 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);
}
}
在这个切面类中,通过对方法调用前后的处理,实现了对返回结果和请求参数的修改。
该类具体包含以下几个部分:
-
类注解:
@Aspect
:表示该类是一个切面类,用于声明该类中包含了切面逻辑。@Component
:表示该类是 Spring 组件,用于让 Spring 容器自动扫描和管理该类。@Order
:表示该切面类的优先级,用于指定组件的加载顺序或切面的执行顺序,优先级高的切面类将先被执行,数值越小,优先级越高,默认值为Integer.MAX_VALUE
。
-
方法注解:
@Pointcut
:定义切入点,即匹配带有@PreProcess
注解的方法。@Around
:表示该方法是一个环绕通知,在方法调用前后都会执行。
-
方法:
-
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
}