关注common wx: CodingTechWork
引言
在开发过程中,会有一些需求将controller层的一些方法入参进行全量转换,最容易想到的可能是在调用下层service方法时,调用公共的方法进行入参转换,这时带来的唯一问题就是代码不雅观,比较冗余。那还有什么方法可以更优雅的解决这个问题吗?答案是有的:切面。
我们实现一个AOP切面程序,对入参中的需转换的参数进行专项转换,而无需在各个controller层的各个方法中进行转换处理。
实践
controller类
package com.test.selfcoding.controller;
import com.test.selfcoding.bean.PersonBean;
import com.test.selfcoding.service.HelloWorldService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @Description controller
* @Author Liao Jy
* @Date 2023/6/28
*/
@RestController
@RequestMapping("/hello/world")
public class HelloWorldController {
@Autowired
private HelloWorldService helloWorldService;
@GetMapping("/test1")
public String testGetHelloWorld() {
return helloWorldService.getHelloWolrd();
}
@PostMapping("/test2")
public String testPostHelloWord(@RequestBody PersonBean personBean) {
return helloWorldService.postHelloWorld(personBean);
}
@GetMapping("/test3")
public String testPostHelloWord(@RequestParam String personName) {
return personName + ", hi world!";
}
}
切面类
package com.test.selfcoding.aspect;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* @Description DefaultArgumentsAspect
* @Author LiaoJy
* @Date 2023/6/28
*/
@Component
@Aspect
@Slf4j
public class DefaultArgumentsAspect {
@Before(value = "execution(* com.test.selfcoding.controller..*(..))")
public void doBefore(JoinPoint joinPoint) {
try {
//获取入参列表
Object[] args = joinPoint.getArgs();
log.info("args: {}", JSON.toJSONString(args));
//入参判空
if (null == args || args.length == 0) {
log.info("no fields!");
return;
}
//获取第一个入参(主要针对@RequestBody,一般只会有一个入参,若遇到多个@RequestParam,需循环处理)
Object arg = args[0];
//获取字段域
Field[] fields = arg.getClass().getDeclaredFields();
log.info("getDeclaredFields: {}", JSON.toJSONString(fields));
//判断入参中是否有"name"
if (Arrays.stream(fields).noneMatch(item -> "name".equals(item.getName()))) {
log.info("no name field!");
return;
}
//入参中有"name",获取该字段
Field field = arg.getClass().getDeclaredField("name");
//判断是否可使用
boolean accessible = ((!Modifier.isPublic(field.getModifiers())
|| !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible());
//若不可用,则需要进行setAccessible
if (accessible) {
ServletRequestAttributes attributes
= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// 获取request对象
HttpServletRequest request = attributes.getRequest();
log.info("method name: {}", request.getRequestURI());
field.setAccessible(true);
//将name参数替换为system
field.set(arg, "system");
}
} catch (Exception e) {
log.error("切面参数设置异常", e);
}
}
}
验证
无参测试接口1
- 接口调用
- 控制台日志
2023-06-28 18:06:06.060 INFO 6495 --- [nio-8001-exec-1] c.t.s.aspect.DefaultArgumentsAspect : args: []
2023-06-28 18:06:06.061 INFO 6495 --- [nio-8001-exec-1] c.t.s.aspect.DefaultArgumentsAspect : no fields!
2023-06-28 18:06:06.066 INFO 6495 --- [nio-8001-exec-1] c.t.s.s.impl.HelloWorldServiceImpl : getHelloWolrd() get str success.
result: hugh, hello world!
- 结果分析
未获取到参数,正常走接口。打印no fields!
,无需转换参数。
不包含指定参数测试接口2
-
接口调用
-
控制台日志
2023-06-28 18:08:04.040 INFO 6495 --- [nio-8001-exec-5] c.t.s.aspect.DefaultArgumentsAspect : args: ["xiaowang"]
2023-06-28 18:08:04.055 INFO 6495 --- [nio-8001-exec-5] c.t.s.aspect.DefaultArgumentsAspect : getDeclaredFields: [{"accessible":false,"annotatedType":{"annotatedGenericComponentType":{"annotations":[],"declaredAnnotations":[],"type":"char"},"annotations":[],"declaredAnnotations":[],"type":"[C"},"annotations":[],"declaringClass":"java.lang.String","enumConstant":false,"genericType":"[C","modifiers":18,"name":"value","synthetic":false,"type":"[C"},{"accessible":false,"annotatedType":{"annotations":[],"declaredAnnotations":[],"type":"int"},"annotations":[],"declaringClass":"java.lang.String","enumConstant":false,"genericType":"int","modifiers":2,"name":"hash","synthetic":false,"type":"int"},{"accessible":false,"annotatedType":{"annotations":[],"declaredAnnotations":[],"type":"long"},"annotations":[],"declaringClass":"java.lang.String","enumConstant":false,"genericType":"long","modifiers":26,"name":"serialVersionUID","synthetic":false,"type":"long"},{"accessible":false,"annotatedType":{"annotatedGenericComponentType":{"annotations":[],"declaredAnnotations":[],"type":"java.io.ObjectStreamField"},"annotations":[],"declaredAnnotations":[],"type":"[Ljava.io.ObjectStreamField;"},"annotations":[],"declaringClass":"java.lang.String","enumConstant":false,"genericType":"[Ljava.io.ObjectStreamField;","modifiers":26,"name":"serialPersistentFields","synthetic":false,"type":"[Ljava.io.ObjectStreamField;"},{"accessible":false,"annotatedType":{"annotatedActualTypeArguments":[{"annotations":[],"declaredAnnotations":[],"type":"java.lang.String"}],"annotations":[],"declaredAnnotations":[],"type":{"actualTypeArguments":["java.lang.String"],"rawType":"java.util.Comparator","typeName":"java.util.Comparator<java.lang.String>"}},"annotations":[],"declaringClass":"java.lang.String","enumConstant":false,"genericType":{"$ref":"$[4].annotatedType.type"},"modifiers":25,"name":"CASE_INSENSITIVE_ORDER","synthetic":false,"type":"java.util.Comparator"}]
2023-06-28 18:08:04.056 INFO 6495 --- [nio-8001-exec-5] c.t.s.aspect.DefaultArgumentsAspect : no name field!
- 结果分析
未获取到指定的参数,正常走接口。打印no name field!
,无需转换参数。
包含指定参数测试接口3
-
接口调用
-
控制台日志
2023-06-28 18:10:15.481 INFO 6495 --- [nio-8001-exec-9] c.t.s.aspect.DefaultArgumentsAspect : args: [{"age":1,"name":"xiaohong"}]
2023-06-28 18:10:15.482 INFO 6495 --- [nio-8001-exec-9] c.t.s.aspect.DefaultArgumentsAspect : getDeclaredFields: [{"accessible":false,"annotatedType":{"annotations":[],"declaredAnnotations":[],"type":"java.lang.String"},"annotations":[],"declaringClass":"com.test.selfcoding.bean.PersonBean","enumConstant":false,"genericType":"java.lang.String","modifiers":2,"name":"name","synthetic":false,"type":"java.lang.String"},{"accessible":false,"annotatedType":{"annotations":[],"declaredAnnotations":[],"type":"int"},"annotations":[],"declaringClass":"com.test.selfcoding.bean.PersonBean","enumConstant":false,"genericType":"int","modifiers":2,"name":"age","synthetic":false,"type":"int"}]
2023-06-28 18:10:15.482 INFO 6495 --- [nio-8001-exec-9] c.t.s.aspect.DefaultArgumentsAspect : method name: /hello/world/test2
- 结果分析
获取到指定的参数,进行name值替换为system
,切面转换参数成功。