起因:需要判断controller层前端传入参数是否为空情况,当时就想到利用SpringAOP来实现,主要有两个思路:
选的是第一种方案,因为第二个方案需要改动代码,而且不太灵活
目前这个小练习还在开发中,gitee地址 https://gitee.com/sunankang/parameter-check 欢迎一起来开发
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterCheckName {
String name() default "";
Class[] cls() default{};
String[] names() default{};
}
如果想判断参数的话
//判断单值
@ParameterCheckName(name = "userName")
public ResultBody get(String userName){}
//判断多值
@ParameterCheckName(names = {"userName","className"})
public ResultBody get(String userName, String className){}
//判断类中某个属性
@ParameterCheckName(cls = Student.class,names={"Student.userName"}){}
public ResultBody get(Student student){}
//混合判断
@ParameterCheckName(clas = Student.class,names={"Student.userName","className"})
public ResultBody get(Student student,String className){}
然后就是利用SpringAop来进行增强
需要将这个类添加到Spring容器当中,否则不生效
@Aspect
@Component
public class AopCheckParameterConfig {
//切入加上注解ParameterCheckName的方法
@Before("@annotation(com.jame.parametercheck.annotation.ParameterCheckName)")
public void test(JoinPoint joinPoint){}
}
首先第一步就是获取切入方法的参数,在网上查询了一下,获取参数的名称的话需要JDK1.8及以上,加入参数javac -parameters
开启此参数可以将编译后的class文件保留原码中的参数名,如果没有开启的话例如get(String name,String className)在运行中可能为
get(String var1,String var2),这样的话就没办法进行参数名称判断了
在idea中添加项目启动参数
然后就是一些正常的逻辑处理
/**
* @author : Jame
* @date : 2021-06-04 16:54
**/
@Aspect
@Component
public class AopCheckParameterConfig {
//存放为空的参数信息
StringBuilder stringBuilder = new StringBuilder();
//切入加上注解ParameterCheckName的方法
@Before("@annotation(com.jame.parametercheck.annotation.ParameterCheckName)")
public void test(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
//获取方法传入的参数
Object[] args = joinPoint.getArgs();
//获取方法传入参数的Class
Class classes[] = new Class[args.length];
for (int i = 0; i < classes.length; i++) {
classes[i] = args[i].getClass();
}
//获取拦截的方法
Method method = signature.getDeclaringType().getDeclaredMethod(signature.getName(), classes);
//获取拦截方法上的注释
ParameterCheckName annotationN = method.getAnnotation(ParameterCheckName.class);
//使用ParameterCheckName进行判断
if (annotationN != null) {
//只传入一个参数名称判断
if (!annotationN.name().equals("")) {
//获取传入参数数组
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].getName().equals(annotationN.name())) {
if (args[i] == null || args[i].equals(""))
stringBuilder.append(parameters[i].getName() + "-");
}
}
//类判断
} else if (annotationN.cls().length != 0) {
String[] names = annotationN.names();
//存放类和类中属性名对应关系
HashMap<String, ArrayList<String>> namesMap = new HashMap<>();
for (int k = 0; k < names.length; k++) {
//需要判断类中属性
if (names[k].contains(".")) {
String[] split = names[k].split("\\.");
if (namesMap.get(split[0]) == null) {
ArrayList<String> strings = new ArrayList<>();
strings.add(split[1]);
namesMap.put(split[0], strings);
} else {
ArrayList<String> strings = namesMap.get(split[0]);
strings.add(split[1]);
namesMap.put(names[0], strings);
}
} else {
parametersCheck(method, args, names[k]);
}
}
//遍历多个类
for (Class c : annotationN.cls()) {
//遍历参数
for (int i = 0; i < args.length; i++) {
if (args[i].getClass() == c) {
//获取具体类的所有属性
Field[] declaredFields = c.getDeclaredFields();
for (Field declaredField : declaredFields) {
ArrayList<String> strings = namesMap.get(c.getSimpleName());
for (String string : strings) {
if (string.equals(declaredField.getName())) {
Method methodAfter = c.getMethod("get" + captureName(declaredField.getName()), null);
//String类型
if (declaredField.getGenericType().toString().equals("class java.lang.String")) {
String s = (String) methodAfter.invoke(args[i]);
if (s == null || s.equals(""))
stringBuilder.append(c.getSimpleName() + "." + declaredField.getName() + "-");
} else if (declaredField.getGenericType().toString().equals("class java.lang.Integer")) {
Integer s = (Integer) methodAfter.invoke(args[i]);
if (s == null)
stringBuilder.append(c.getSimpleName() + "." + declaredField.getName() + "-");
}
}
}
}
}
}
}
}
//多个参数名判断
if (annotationN.cls().length == 0 && annotationN.names().length != 0) {
String[] names = annotationN.names();
for (String name : names) {
parametersCheck(method, args, name);
}
}
}
//抛出异常
if (stringBuilder.length() != 0) {
String s = stringBuilder.toString();
//清空字符串
stringBuilder.delete(0, stringBuilder.length());
System.out.println(stringBuilder.toString());
throw new ParameterException(666, "参数:" + s + "为空");
}
}
public void parametersCheck(Method method, Object[] args, String name) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].getName().equals(name)) {
if (args[i] == null || args[i].equals("")) {
stringBuilder.append(parameters[i].getName() + "-");
}
}
}
}
private String captureName(String str) {
// 进行字母的ascii编码前移,效率要高于截取字符串进行转换的操作
char[] cs = str.toCharArray();
cs[0] -= 32;
return String.valueOf(cs);
}
}
整体逻辑如下
最后使用有SpringBoot全局异常处理来接收抛出的自定义异常
@ControllerAdvice
public class MyExceptionHandler{
@ResponseBody
@ExceptionHandler(value = ParameterException.class)
public ResultBody exceptionHandler(ParameterException e){
ResultBody resultBody = new ResultBody();
resultBody.setMessage(e.getMessage());
resultBody.setCode(e.getCode());
return resultBody;
}
}
但是呢,目前这个项目还有很多问题:
1.现在还不能判断基本数据类型
2.如果传入Integer的参数为空,就直接报400,试过在Mapping添加required=false,JoinPoint获取不到这个参数(主要解决)
Controller层
@GetMapping("/get")
@ParameterCheckName(names = {"id","name"})
public String get(@RequestParam(value = "id" ) Integer id,
@RequestParam(value = "name" ) String name) {
return "完成,返回结果";
}
切入类
public void test(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
//
Object[] args = joinPoint.getArgs();
}
String类型没有传入可以检测到
而Integer不传入则报错400,在Mapping添加required=false
获取不到Integer的参数,就很难受,大家有什么解决方案可以来一起开发
https://gitee.com/sunankang/parameter-check
原文:https://www.cnblogs.com/sunankang/p/14855403.html