zoukankan      html  css  js  c++  java
  • Java Bean Validation学习笔记

    在Java项目开发当中,数据校验是经常遇到的问题,为此要写上一大串的代码进行校验,这样就会导致代码冗余和一些管理的问题。那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的。(就像 ASP.NET MVC 的Model中就是使用数据标记(Data Annotations)这种属性来进行验证。)

    JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,官方参考实现是Hibernate Validator。
    此实现与Hibernate ORM 没有任何关系。JSR 303 用于对Java Bean 中的字段的值进行验证。

    JSR 303内置的约束规则

    @AssertTrue / @AssertFalse

    验证适用字段:boolean
    注解说明:验证值是否为true / false
    属性说明:- 
    

    @DecimalMax / @DecimalMin

    验证适用字段:BigDecimal,BigInteger,String,byte,short,int,long
    注解说明:验证值是否小于或者等于指定的小数值,要注意小数存在精度问题
    属性说明:公共 
    

    @Digits

    验证适用字段:BigDecimal,BigInteger,String,byte,short,int,long
    注解说明:验证值的数字构成是否合法
    属性说明:integer:指定整数部分的数字的位数。fraction: 指定小数部分的数字的位数。 
    

    @Future / @Past

    验证适用字段:Date,Calendar
    注解说明:验证值是否在当前时间之后 / 之前
    属性说明:公共 
    

    @Max / @Min

    验证适用字段:BigDecimal,BigInteger,String,byte,short,int,long
    注解说明:验证值是否小于或者等于指定的整数值
    属性说明:公共 
    

    @NotNull / @Null

    验证适用字段:引用数据类型
    注解说明:验证值是否为非空 / 空
    属性说明:公共 
    

    @Pattern

    验证适用字段:String
    注解说明:验证值是否配备正则表达式
    属性说明:regexp:正则表达式flags: 指定Pattern.Flag 的数组,表示正则表达式的相关选项。 
    

    @Size

    验证适用字段:String,Collection,Map,数组
    注解说明:验证值是否满足长度要求
    属性说明:max:指定最大长度,min:指定最小长度。 
    

    @Valid

    验证适用字段:引用类型
    注解说明:验证值是否需要递归验证
    属性说明:无 
    

    添加JAR包依赖

    在pom.xml中添加如下依赖:

    <!--jsr 303-->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <!-- hibernate validator-->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.2.0.Final</version>
    </dependency>
    

    校验一般使用

    UserModel实体定义

    public class UserModel {
        @Range(min = 20, max = 50, message = "age应该在[20,50]之间")
        private Integer age;
    
        @NotNull(message = "name不能为空")
        private String name;
    
        @Length(max = 100, message = "address不能超过100")
        private String address;
    
        @Email(message = "email格式不对")
        private String email;
        
        //getter setter 方法.......
    }
    

    代码内手动验证

    //获得验证器
    Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    
    UserModel user = new UserModel();
    //执行验证
    Set<ConstraintViolation<UserModel>> validators = validator.validate(user);
    for (ConstraintViolation<UserModel> constraintViolation : validators) {
        System.out.println(constraintViolation.getMessage());
    }
    

    Controller中开启验证加上注解@Validated,SpringMVC会自动完成校验并返回结果

        @RequestMapping(method = RequestMethod.POST)
        public UserModel create(@RequestBody @Validated UserModel user) {
            return userService.create(user);
        }
        
       
    

    如果不想抛出异常,想返回校验信息给前端,这个时候就需要用到BindingResult了

        @PostMapping
        public UserModel create(@Valid @RequestBody UserModel user, BindingResult errors){
            if(errors.hasErrors()){
                errors.getAllErrors().stream().forEach(error-> System.out.println(error.getDefaultMessage()));
            }
            //业务处理...
            return user;
        }
    

    SpringMVC自定义校验异常结果,有时候自动完成的校验返回的结果不是我们需要的数据格式,我们可以自定义这个异常结果。

    @ControllerAdvice
    public class ValidationResponseAdvice extends ResponseEntityExceptionHandler {
        @Override
        protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
            String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.joining("
    "));
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("code","400");
            map.put("error","Bad Request");
            map.put("message",message);
            map.put("errors",ex.getBindingResult().getAllErrors());
            return ResponseEntity.badRequest().body(JsonUtils.toJson(map));
        }
    }
    

    自定义校验注解

    jSR303和Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要自定义校验注解。

    1、定义注解,message、groups和payload三个属性是必须定义的。

    //省略引用
    
    /**
     * 自定义参数校验注解
     * 校验 List 集合中是否有null 元素
     */
    
    @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(validatedBy = ListNotHasNullValidatorImpl.class)////此处指定了注解的实现类为ListNotHasNullValidatorImpl
    
    public @interface ListNotHasNull {
    
        /**
         * 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义
         */
        int value() default 0;
    
        String message() default "List集合中不能含有null元素";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        /**
         * 定义List,为了让Bean的一个属性上可以添加多套规则
         */
        @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
        @Retention(RUNTIME)
        @Documented
        @interface List {
            ListNotHasNull[] value();
        }
    }
    

    2、注解实现类:

    //引用省略...
    
    /**
     * 自定义注解ListNotHasNull 的实现类
     * 用于判断List集合中是否含有null元素
     */
    
    @Service
    public class ListNotHasNullValidatorImpl implements ConstraintValidator<ListNotHasNull, List> {
    
        private int value;
    
        @Override
        public void initialize(ListNotHasNull constraintAnnotation) {
            //传入value 值,可以在校验中使用
            this.value = constraintAnnotation.value();
        }
    
        public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
            for (Object object : list) {
                if (object == null) {
                    //如果List集合中含有Null元素,校验失败
                    return false;
                }
            }
            return true;
        }
    
    }
    

    3、model添加注解:

    public class User {
    
        //其他参数 .......
    
        /**
         * 所拥有的书籍列表
         */
        @NotEmpty(message = "所拥有书籍不能为空")
        @ListNotHasNull(message = "List 中不能含有null元素")
        @Valid
        private List<Book> books;
        //getter setter 方法.......
    }
    

    分组验证

    对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下

    1、定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则

    /**
     * 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则
     */
    public interface PersonAddView {
    }
    
    /**
     * 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
     */
    public interface PersonModifyView {
    }
    

    2、Model上添加注解时使用指明所述的分组

    public class Person {
        private long id;
        /**
         * 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
         */
        @NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message = "添加、修改用户时名字不能为空", payload = ValidateErrorLevel.Info.class)
        @ListNotHasNull.List({
                @ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),
                @ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})
        private String name;
    
        @NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
        private String address;
    
        @Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")
        @Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
        private int age;
      //getter setter 方法......
    }
    

    3、启用校验

    此时启用校验和之前的不同,需要指明启用哪一组规则

    /**
         * 添加一个Person对象
         * 此处启用PersonAddView 这个验证规则
         * 备注:此处@Validated(PersonAddView.class) 表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,
         * 若两个规则同时加上去,则只有第一套起作用
         */
        @RequestMapping(value = "/person", method = RequestMethod.POST)
        public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {
            System.out.println(person.toString());
        }
    
        /**
         * 修改Person对象
         * 此处启用PersonModifyView 这个验证规则
         */
        @RequestMapping(value = "/person", method = RequestMethod.PUT)
        public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) {
            System.out.println(person.toString());
        }
    

    参考:

    https://yingzhuo.iteye.com/blog/969444

    https://www.cnblogs.com/beiyan/p/5946345.html

  • 相关阅读:
    HDR算法(一)
    机器视觉话题入门资料---能看懂大致原理再去纠细节
    WRT callback
    dependency of static library
    dll 和 lib--初级
    memory corruption
    Python~函数的参数
    Python~函数
    Python~if,while,for~顺序,判断,循环
    Python~list,tuple^_^dict,set
  • 原文地址:https://www.cnblogs.com/songshuai/p/11044457.html
Copyright © 2011-2022 走看看