自定义正则表达式校验注解
创始人
2024-03-24 18:56:07
0

1. 解释

自定义注解,通过正则表达式来校验请求相关参数,也可用于校验是否可空等

2. 自定义注解

1. @Check

自定义@Check 注解用来当做AOP切点

package net.lesscoding.aop;import java.lang.annotation.*;/*** @author eleven* @date 2022/11/30 11:56* @description*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Check {public String value() default "";
}

2. @CheckProperties

自定义 @CheckProperties注解用来检验请求上的多个参数

package net.lesscoding.aop;import net.lesscoding.aop.CheckProperty;import java.lang.annotation.*;/*** @author eleven* @date 2022/11/30 15:23* @description*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckProperties {/**** 属性名* @return {@code String}*/String value() default "";/*** 检查的属性值** @return {@code CheckProperty[]}*/CheckProperty[] checks() default {};/***  下标* @return int*/int index() default 0;
}

3. @CheckProperty

自定义注解@CheckProperty用来校验单个属性

package net.lesscoding.aop;import net.lesscoding.enums.TimeType;import java.lang.annotation.*;/*** @author eleven* @date 2022/11/30 10:30* @description 只允许在属性和参数上添加*/
@Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckProperty {/** 注解描述 */public String value() default "";/** 报错提示信息 */public String message() default "";/** 正则表达式 */public String regexp() default "";/** 校验的下标 */public int index() default -1;/** 时间格式 */public String pattern() default "";/** 时间类型 */public TimeType timetype() default TimeType.NON_TIME;/** notNull */public boolean notNull() default false;/** notBlank */public boolean notBlank() default false;
}

3. 其他相关类

1. 时间类型枚举类

这个类现在还没有用到,时间类型一般接收过来的时候就会有 @@JsonFormat@DateTimeFormat注解约束类型,所以现在用不到

package net.lesscoding.enums;/*** @author eleven* @date 2022/12/5 10:05* @description*/
public enum TimeType {/*** LocalTime*/LOCAL_TIME,/*** LocalDateTime*/LOCAL_DATETIME,/*** LocalTime*/LOCAL_DATE,/*** Date 类型*/DATE,/*** Timestamp*/TIMESTAMP,/*** 非时间类型*/NON_TIME;
}

2. 注解处理类

如果方法上有@Check注解 ,则进行下边的操作

  1. 首先校验方法参数里是否有@CheckProperty注解,有的话则校验相关参数是否符合要求
  2. 校验方法上是否有@CheckProperties注解,有的话按个校验里边的@CheckProperty注解判断参数是否符合正则表达式(适用于一个接口有好多个参数的情况
  3. 如果方法上没有@CheckProperties注解,但是有@CheckProperty注解,校验相关的参数是否符合
  4. 方法上只有@Check注解,但是传入的实体类的属性上有@CheckProperty挨个校验方法参数是否符合
package net.lesscoding.aop;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import net.lesscoding.enums.TimeType;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** @author eleven* @date 2022/11/30 11:17* @description*/
@Aspect
@Component
@Slf4j
public class CheckAspect {private long methodIntervalMs = 0L;@Pointcut("@annotation(net.lesscoding.aop.Check)")public void checkPointCut() {}@Before(value = "checkPointCut()")public void test(JoinPoint joinPoint) throws Exception {Check check = getAnnotationCheck(joinPoint);//获取方法,此处可将signature强转为MethodSignatureMethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();checkArgsAnnotation(joinPoint,method);checkOverMethodAnnotation(joinPoint,method);checkInField(joinPoint);}/*** 校验在类属性里的注解* @param joinPoint     切点* @param method        方法*/private void checkInField(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();for (Object arg : args) {Class argClass = arg.getClass();Field[] declaredFields = argClass.getDeclaredFields();for (int i = 0; i < declaredFields.length; i++) {Field field = declaredFields[i];CheckProperty checkProperty = field.getAnnotation(CheckProperty.class);if (checkProperty == null){continue;}Object argByFiled = getArgByFiled(field, arg);checkArg(checkProperty,arg.getClass(),field.getName(),argByFiled);}}}/*** 根据类定义的field获取值* @param field     类属性* @param arg       参数* @return  Object*/protected Object getArgByFiled(Field field,Object arg){try{PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(),arg.getClass());Method readMethod = propertyDescriptor.getReadMethod();Object invoke = readMethod.invoke(arg);return invoke;}catch (Exception e){e.printStackTrace();}return new Object();}/*** 校验方法上的注解* @param joinPoint*/protected void checkOverMethodAnnotation(JoinPoint joinPoint,Method method) {Parameter[] parameters = method.getParameters();Object[] args = joinPoint.getArgs();// 判断多个注解配置CheckProperties checkProperties = method.getAnnotation(CheckProperties.class);if(checkProperties != null){CheckProperty[] checkArr = checkProperties.checks();if(checkArr.length == 0){return;}for (int i = 0; i < checkArr.length; i++) {CheckProperty checkProperty = checkArr[i];int index = checkProperty.index();Parameter parameter = parameters[index != -1 ? index : i];Object arg = args[index != -1 ? index : i];checkArg(checkProperty, parameter, arg);}}// 校验 @CheckProperty 注解CheckProperty checkProperty = method.getAnnotation(CheckProperty.class);if(checkProperty != null){int index = checkProperty.index();Parameter parameter = parameters[index != -1 ? index : 0];Object arg = args[index != -1 ? index : 0];checkArg(checkProperty,parameter,arg);}}/*** 检查方法参数里的注解* @param joinPoint AOP切点*/protected void checkArgsAnnotation(JoinPoint joinPoint, Method method){// 获取参数Parameter[] parameters = method.getParameters();//参数注解,1维是参数,2维是注解Annotation[][] annotations = method.getParameterAnnotations();Object[] args = joinPoint.getArgs();for (int i = 0; i < annotations.length; i++) {Annotation[] annotation = annotations[i];List paramsAnnotation = new ArrayList<>(Arrays.asList(annotation));int finalI = i;paramsAnnotation.stream().forEach(item -> {Class annotationType = item.annotationType();if(annotationType.getName().endsWith(".CheckProperty")){CheckProperty checkProperty = (CheckProperty)item;checkArg(checkProperty,parameters[finalI],args[finalI]);}});}}/*** 校验属性* @param checkProperty     注解内容* @param parameter         参数描述* @param arg               参数*/protected void checkArg(CheckProperty checkProperty,Parameter parameter,Object arg){checkArg(checkProperty,parameter.getType(),parameter.getName(),arg);}/*** 校验属性* @param checkProperty     注解* @param type              参数类型* @param argName           参数名称* @param arg               参数*/protected void checkArg(CheckProperty checkProperty,Class type ,String argName,Object arg){// 获取提示信息String message = checkProperty.message();String regexp = checkProperty.regexp();if(checkProperty.notNull()){if(null == arg ) {throw new IllegalArgumentException(String.format("参数[%s]不能为空: %s", argName, message));}}String argStr = String.valueOf(arg);if(checkProperty.notBlank() && type.equals(String.class)){if(StrUtil.isBlank(argStr)){throw new IllegalArgumentException(String.format("参数[%s]不能为空: %s", argName, message));}}if(StrUtil.isNotBlank(regexp)){if(!ReUtil.isMatch(regexp,argStr)){throw new IllegalArgumentException(String.format("参数 [ %s ] :: [ %s ] 不合法,%s",argName,argStr,checkProperty.message()));}}}/*** 环绕通知,可以用来计算方法耗时* @param joinPoint* @throws Exception*///@Around(value = "checkPointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 定义返回对象、得到方法需要的参数Object[] args = joinPoint.getArgs();TimeInterval timer = DateUtil.timer();Object obj = joinPoint.proceed(args);MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getDeclaringTypeName() + "." + signature.getName();log.info("----------{}方法开始执行----------",methodName);// 打印耗时的信息methodIntervalMs = timer.intervalMs();log.info("----------{}执行耗时{}ms----------", methodName, methodIntervalMs);log.info("----------{}返回参数----------\n{}", methodName, new Gson().toJson(obj));return obj;}/*** 是否存在注解,如果存在就获取*/private Check getAnnotationCheck(JoinPoint joinPoint) throws Exception {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {return method.getAnnotation(Check.class);}return null;}
}

3. 通用返回类

package net.lesscoding.common;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author eleven* @date 2022/11/30 12:12* @description*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {private Integer code;private String message;private Object data;
}

4. 测试类

package net.lesscoding.entity;import lombok.Data;
import net.lesscoding.aop.CheckProperty;/*** @author eleven* @date 2022/11/30 10:34* @description*/
@Data
public class TestEntity {@CheckProperty(message = "超过字符串最大长度",regexp = "\\w{0,12}")private String strTest;@CheckProperty(message = "超过最大长度4",regexp = "\\d{0,4}")private Integer intTest;private Double doubleTest;}

5. 正则表达式常量类

package net.lesscoding.consts;/*** @author eleven* @date 2022/11/30 10:38* @description* 相关正则表达式可以参考* https://jex.im/regulex/#!flags=&re=%5E(a%7Cb)*%3F%24* https://regexr.com/*/
public class RegexpConst {/*** 最大长度32*/public static final String STR_MAX_LENGTH_32 = "\\w{0,32}";/*** 整数最大长度4*/public static final String INTEGER_MAX_LENGTH_4 = "\\d{0,4}";/*** 无符号整数最大长度4*/public static final String UNSIGNED_INTEGER_MAX_LENGTH_4 = "-*\\d{0,4}";/*** 两位小数*/public static final String DECIMAL_PLACE_2 = "\\d+.\\d{0,2}";}

4. 测试接口

package net.lesscoding.controller;import net.lesscoding.aop.Check;
import net.lesscoding.aop.CheckProperties;
import net.lesscoding.aop.CheckProperty;
import net.lesscoding.common.Result;
import net.lesscoding.consts.RegexpConst;
import net.lesscoding.entity.TestEntity;
import org.springframework.web.bind.annotation.*;/*** @author eleven* @date 2022/11/30 12:09* @description*/
@RestController
@RequestMapping("/check")
public class CheckTestController {@GetMapping("/inArgs")@Checkpublic Result inArgs(@CheckProperty(message = "长度超过32位",regexp = RegexpConst.STR_MAX_LENGTH_32) @RequestParam("str") String str,@CheckProperty(message = "长度超过4位",regexp = RegexpConst.INTEGER_MAX_LENGTH_4) @RequestParam("index") Integer index){return new Result(200,"success",str + " :: " +  index);}@GetMapping("/overMethodsMulti")@Check@CheckProperties(checks = {@CheckProperty(message = "长度超过32位",regexp = RegexpConst.STR_MAX_LENGTH_32,index = 0),@CheckProperty(message = "长度超过4位",regexp = RegexpConst.INTEGER_MAX_LENGTH_4,index = 1)})public Result overMethodsMulti( String str, Integer index){return new Result(200,"success",str + " :: " + index);}@GetMapping("/overMethods")@Check@CheckProperty(message = "长度超过32位",regexp = RegexpConst.STR_MAX_LENGTH_32,index = 0)public Result overMethods( String str,Integer index){return new Result(200,"success",str + " :: " + index);}@GetMapping("/inFields")@Checkpublic Result inFields(TestEntity testEntity){return new Result(200,"success",testEntity);}@PostMapping("/inFields")@Checkpublic Result inFieldsPost(@RequestBody TestEntity testEntity){return new Result(200,"success",testEntity);}
}

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...