Spring boot 通过切面,实现超灵活的注解式数据校验

2022-07-10 14:28:24

在企业系统的开发中,用户表单输入的场景是会经常遇见的,如何让数据校验脱离于业务代码逻辑,谁也不想在逻辑代码里对字段逐一判断。。。。

Spring MVC的校验方式

在使用Spring MVC时的时候,直接使用hibernate-validator的注解,如下:

publicclassUser {private Long id;@NotBlank(message ="name不能为空")@Size(min =5, max =10, message ="字符在5到10个")private String name;private String des;@NotNull@Max(value =3, message ="type 参数错误")@Min(value =0, message ="type 参数错误")private Integer type;@Min(value =0, message ="参数错误, limit必须大于或等于0")privateint limit;@Pattern(regexp ="^(true|false)$", message ="参数错误, 参数isActive只能是true或者false")private String flag;// setters and getters

然后将User对象作为Controller的参数,交给Spring MVC去帮你校验。

通过切面实现自己的注解式数据校验

这是一个SOA的微服务应用,没有controller和Spring MVC,当然也没有所谓的容器(Tomcat、Jetty),对于来自于client的调用,也要进行参数校验。继续基于hibernate-validator,
参看validator的官方文档:
http://hibernate.org/validator/documentation/getting-started/
和链接:https://diamondfsd.com/article/78fa12cd-b530-4a90-b438-13d5a0c4e26c (灵感来自于此)
引入依赖:

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator-cdi</artifactId><version>5.4.1.Final</version></dependency><dependency><groupId>org.glassfish</groupId><artifactId>javax.el</artifactId><version>3.0.1-b08</version></dependency>

这里需要引入spring boot和aop的一些知识点,自行去网上google吧。我直接上代码了,谁叫我是代码的搬运工。
定义一个切面:

@Aspect//一个切面@Configuration// spring boot 配置类publicclassRequestParamValidAspect {privatefinal ValidatorFactory factory = Validation.buildDefaultValidatorFactory();privatefinal ExecutableValidator methodValidator = factory.getValidator().forExecutables();privatefinal Validator beanValidator = factory.getValidator();private <T> Set<ConstraintViolation<T>>validMethodParams(T obj, Method method, Object [] params){return methodValidator.validateParameters(obj, method, params);
    }private <T> Set<ConstraintViolation<T>>validBeanParams(T bean) {return beanValidator.validate(bean);
    }@Pointcut("execution(* com.jiaobuchong.commodity.service.*.*(..))")publicvoidsoaServiceBefore(){}/* * 通过连接点切入 */@Before("soaServiceBefore()")publicvoidtwiceAsOld1(JoinPoint point) {//  获得切入目标对象
        Object target = point.getThis();// 获得切入方法参数
        Object [] args = point.getArgs();// 获得切入的方法
        Method method = ((MethodSignature)point.getSignature()).getMethod();// 校验以基本数据类型 为方法参数的
        Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args);

        Iterator<ConstraintViolation<Object>> violationIterator = validResult.iterator();while (violationIterator.hasNext()) {// 此处可以抛个异常提示用户参数输入格式不正确
            System.out.println("method check---------" + violationIterator.next().getMessage());
        }// 校验以java bean对象 为方法参数的for (Object bean : args) {if (null != bean) {
                validResult = validBeanParams(bean);
                violationIterator = validResult.iterator();while (violationIterator.hasNext()) {// 此处可以抛个异常提示用户参数输入格式不正确
                    System.out.println("bean check-------" + violationIterator.next().getMessage());
                }
            }
        }
    }
}

具体的Service

// DemoService.javapublicinterfaceDemoService {void one(@NotNull(message ="不能为null") Integer a,@NotBlank String b);void two(@NotNull(message ="paramsVo不能为null") ParamsVo paramsVo,@NotNull(message ="go不能为null") String go);
}// ParamsVo.javapublicclassParamsVo {@NotBlank(message ="不能为空")private String name;@NotBlank@Length(min =2, max =20, message ="不可以为空,最多20个字")private String desc;@NotNull@Valid// 需要加上@Valid注解,不然不会校验到Img对象private List<Img> imgList;@NotNull(message ="length不能为null")@Range(min =3, max =100, message ="长度范围3-100")private Integer length;// omitted other code
}publicclassImg {@NotNull(message ="img id 不能为null")private Long id;@NotBlank(message ="img name 不能为空")private String name;// omitted other code
}

运行DemoService:

@Autowiredprivate DemoService demoService;@TestpublicvoidtestGo() {
        demoService.one(null,"");

        ParamsVo paramsVo =new ParamsVo();
        List<Img> list =new ArrayList<>();
        Img img =new Img();
        list.add(img);
        paramsVo.setImgList(list);
        paramsVo.setDesc("你");
        paramsVo.setLength(1);
        demoService.two(paramsVo,null);
    }

运行结果:

method check———不能为空
method check———不能为null
method check———go不能为null
bean check——-img name 不能为空
bean check——-不能为空
bean check——-深度范围3-100
bean check——-img id 不能为null
bean check——-不可以为空,最多20个字

这样比Spring MVC的校验功能还强大了,

// Spring MVC中对下面这样的校验是没有作用的void one(@NotNull(message ="不能为null") Integer a,@NotBlank String b);

经过一番改造后,啥都支持了。而且独立于业务逻辑,维护和新增校验都很方便,代码量也变少了!

人生苦短,so less code!

  • 作者:Balalalalalalalala
  • 原文链接:https://blog.csdn.net/jiaobuchong/article/details/74094155
    更新时间:2022-07-10 14:28:24