使用注解、切面、反射,对前端传递的参数,做统一的空格(trim)处理
使用注解、切面、反射,对前端传递的参数,做统一的空格(trim)处理
业务场景:由于目前的前后端分离,在某些情况下,前端可能没有对传入后端的字符串做出来,导致传递到后端的数据可能有偏差,两端出现空格的情况。所有我就想,怎么能够统一的对前端的传参做一个处理,让后端不用太关心前端的参数问题。
实现思路:前端的参数一般都放在请求体中,作为后端的一个入参。我们需要做的就是,把前端的参数的空格去除。所以我们需要获取到需要处理的方法的入参。拿到入参后并且要把,它的值给它改变。
实现方法:使用注解对需要处理的方法,加上注解,作为一个标志。然后通过这个标志,我们定义一个切入点。对它进行环绕增强。再利用反射,动态的去设值到原DTO中。
实现步骤:
- 定义一个标志注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoString {
}
- 为方法加上@AutoString 注解
@AutoString
@ApiOperation("test01")
@PostMapping("/test01")
public Object test01(@RequestBody TestDto testDto){
System.out.println(testDto);
return testDto;
}
@AutoString
@ApiOperation("test02")
@PostMapping("/test02")
public Object test02(@RequestBody Test02Dto test02Dto){
System.out.println(test02Dto);
return test02Dto;
}
- 定义测试的DTO,包含一些常用的类型
public class Test02Dto {
private String name;
private Integer size;
private Double weight;
private Boolean activeFlag;
private List<String> ids;
@Override
public String toString() {
return "Test02Dto{" +
"name='" + name + '\'' +
", size=" + size +
", weight=" + weight +
", activeFlag=" + activeFlag +
", ids=" + ids +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
public Boolean getActiveFlag() {
return activeFlag;
}
public void setActiveFlag(Boolean activeFlag) {
this.activeFlag = activeFlag;
}
public List<String> getIds() {
return ids;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
}
- 通过反射对原值进行处理,为了大家阅读方便,我每行都打上了注解
package com.example.demo.aop;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
/**
* 系统日志,切面处理类
*
* @author jie
* @date 2019/12/23 -13:36
*/
@Aspect
@Component
public class AutoStringAspect {
// 通过 Pointcut 获取注解位置
@Pointcut("@annotation(com.example.demo.annotation.AutoString)")
public void stringPointCut() {
}
// 增强处理
@Around("stringPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
//获取参数值
Object[] paramValues = point.getArgs();
for (Object arg : paramValues) {
//获取类的字段
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
// 设置字段可访问, 否则无法访问private修饰的变量值
field.setAccessible(true);
//获取字段名
String name = field.getName();
// 获取字段类型
String type = field.getGenericType().toString();
// 拼装get set方法 例如 setName getName
String setMethodName = "set" + name.substring(0,1).toUpperCase()+name.substring(1);
String getMethodName = "get" + name.substring(0,1).toUpperCase()+name.substring(1);
//通过方法名称获取对应的方法,这里是获取字段的get方法
Method getMethod = arg.getClass().getMethod(getMethodName);
// 判断类型是string 类型
if ("class java.lang.String".equals(type)) {
// 获取set方法
Method setMethod = arg.getClass().getMethod(setMethodName, String.class);
// 通过 invoke 获取到get方法的Value值 PS: invoke()方法就是用来执行指定对象的方法
String value = (String) getMethod.invoke(arg);
// 再invoke 执行 set方法 StringUtils.trim 对空格进行处理
setMethod.invoke(arg, StringUtils.trim(value));
}
// 判断类型是List<String> 类型
if ("java.util.List<java.lang.String>".equals(type)) {
Method setMethod = arg.getClass().getMethod(setMethodName, List.class);
List<String> valueList = (List<String>) getMethod.invoke(arg);
// 通过java 8 对集合中的值进行处理 ,java8 中 “::” 就是调用方法的意思
List<String> collect = valueList.stream().map(StringUtils::trim).collect(Collectors.toList());
setMethod.invoke(arg, collect);
}
}
}
return point.proceed();
}
}