Springboot AOP(面向切面)之自定义实体类校验注解 使用BindingResult校验


七龙 发布于 2024-06-17 / 31 阅读 / 0 评论 /
BindingResult是用来校验参数的可用参数如下所示: @Null 只能是null @NotNull 不能为null 注意用在基本类型上无效,基本类型有默认初始值 @AssertFalse 必须为false @AssertTrue 必须是true 字符串/数组/集合检查:(字符串本身就是个数组

BindingResult是用来校验参数的可用参数如下所示: @Null 只能是null @NotNull 不能为null
注意用在基本类型上无效,基本类型有默认初始值 @AssertFalse 必须为false @AssertTrue 必须是true

字符串/数组/集合检查:(字符串本身就是个数组) @Pattern(regexp="reg") 验证字符串满足正则 @Size(max,
min) 验证字符串、数组、集合长度范围 @NotEmpty 验证字符串不为空或者null @NotBlank
验证字符串不为null或者trim()后不为空

数值检查:同时能验证一个字符串是否是满足限制的数字的字符串 @Max 规定值得上限int @Min 规定值得下限
@DecimalMax("10.8") 以传入字符串构建一个BigDecimal,规定值要小于这个值 @DecimalMin
可以用来限制浮点数大小 @Digits(int1, int2) 限制一个小数,整数精度小于int1;小数部分精度小于int2 @Digits
无参数,验证字符串是否合法 @Range(min=long1,max=long2) 检查数字是否在范围之间 这些都包括边界值

日期检查:Date/Calendar @Post 限定一个日期,日期必须是过去的日期 @Future 限定一个日期,日期必须是未来的日期

其他验证: @Vaild 递归验证,用于对象、数组和集合,会对对象的元素、数组的元素进行一一校验 @Email
用于验证一个字符串是否是一个合法的右键地址,空字符串或null算验证通过
@URL(protocol=,host=,port=,regexp=,flags=) 用于校验一个字符串是否是合法UR

那么我们可以简化,使用自己写的一个注解注入到服务层,让其自己完成必须参数的校验功能
maven项目引入

public class Parameter {

@NotEmpty(message="姓名不能为空")
    private String name;
    
    @Min(value = 18, message = "年龄必须大于18岁")
    private int age;
    
    @NotEmpty(message="hobbies不能为空")
    private List<String> hobbies;

}

建一个实体类对象

/**检查方法实体类是否符合规则
 * @author qilong
 */
//在自定义注解的时候可以使用@Documented来进行标注,如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来。
@Documented 
//@Retention可以用来修饰注解,是注解的注解,称为元注解。RetentionPolicy.RUNTIME运行时注解,注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Retention(RetentionPolicy.RUNTIME) 
//注解的作用目标METHOD——方法,TYPE——接口、类、枚举、注解
@Target({ElementType.METHOD, ElementType.TYPE}) 
public @interface EntityCheck {
    /**
     * 是否打印输出拦截日志
     * @return
     */
    boolean debug() default false;

}

新建一个自定义注解接口

/**实体类检查切面
 * @author qilong
 */
@Aspect
@Component
@Slf4j
public class EntityCheckAspect {

     //之前注解接口的路径
    @Pointcut("@annotation(zx.cloud.commons.utils.entityCheck.EntityCheck)")
    public void pointEntityCheck() {
    }

    /**
     * 环绕切入点
     * 匹配指定包包下所有使用@Service注解的类
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "pointEntityCheck()")
    private Object entityCheck(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取切入的方法对象
        // 这个m是代理对象的,没有包含注解
        Method m = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        // this()返回代理对象,target()返回目标对象,目标对象反射获取的method对象才包含注解
        Method methodWithAnnotations = proceedingJoinPoint.getTarget().getClass().getDeclaredMethod(
                proceedingJoinPoint.getSignature().getName(), m.getParameterTypes());
        EntityCheck annotation = methodWithAnnotations.getAnnotation(EntityCheck.class);
        if (annotation.debug()) {
            log.info("实体类验证:拦截到包:{} 方法:{}", proceedingJoinPoint.getTarget().getClass().getName(), proceedingJoinPoint.getSignature().getName());
        }
        //获取所有参数
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object o: args) {
            if (o instanceof BindingResult){
                //校验不通过如果有错误 不执行方法直接返回出去
                String re = getError((BindingResult)o);
                if (null != re) {
                    //NormalResponse 是我自己的返回的格式,你可以自定义,例如返回一个map,String 都可以
                    return new NormalResponse(Code.FAIL, re);
                }
            }

        }
        //参数校验没有错误继续执行方法
        return proceedingJoinPoint.proceed(args);
    }


    //自定义错误返回, 当实体类校验不通过,这里会直接返回给前端
    public static String getError(BindingResult result) {
        StringBuffer errorInfo = new StringBuffer();
        if (result.hasErrors()) {
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError error : fieldErrors) {
                errorInfo.append(String.format("参数 %s错误: %s ", error.getField(), error.getDefaultMessage()));
            }
            return errorInfo.toString();
        } else {
            return null;
        }
    }

}

新建一个类用以实现AOP

@Slf4j
@Service
public class TestServiceImpl implements TestService {
//加上我们自定义的注解
 @EntityCheck
    @Override
    public NormalResponse createAutoProvisioningGroup(Parameter entity, BindingResult result) {
    log.info("没有遇到参数错误我已被运行!");
    }
}

服务层

@Slf4j
@Service
public class TestServiceImpl implements TestService {
//加上我们自定义的注解
 @EntityCheck
    @Override
    public NormalResponse createAutoProvisioningGroup(Parameter entity, BindingResult result) {
    log.info("没有遇到参数错误我已被运行!");
    }
}

访问你的接口,如果有参数不对,就不会执行服务层,被@EntityCheck注解检查后拦截返回,至此自定义参数校验注解完成!



评论