• bean-validation----hibernate-validator校验框架整理【未完待续】


    代码简洁之道 beanvalidationhibernate-validator

    序言

    beanvalidation官网:https://beanvalidation.org/ api的接口

    Hibernate-validator官网:http://hibernate.org/validator/ api的实现

    image-20220906215857367

    一、传统的参数校验

    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    /**
     * 用户信息
     *
     * @author zs
     * @date 2022-09-06
     */
    @Data
    public class UserInfo {
        private Long id;
        /**
         * 不能是null, "", "   "
         */
        private String name;
        /**
         * 正整数, 1-800
         */
        private Integer age;
        /**
         * email的格式
         */
        private String email;
        /**
         * 符合中国大陆手机号
         */
        private String phone;
        /**
         * 不能超过当前日期
         */
        private LocalDateTime birthday;
        /**
         * url
         */
        private String personalPage;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    public class TraditionalTest {
    
    
        @Test
        public void test01(){
            UserInfo userInfo = new UserInfo();
            validateUserInfo(userInfo);
        }
    
        private static void validateUserInfo(UserInfo userInfo){
            // 用户名校验
            String name = userInfo.getName();
            if (name == null || "".equals(name) || "".equals(name.trim())) {
                //不符合校验规则
                throw new RuntimeException("name 不符合校验规则");
            }
    
            // age校验
            Integer age = userInfo.getAge();
            boolean ageValidate = age > 1 && age < 800;
            if (!ageValidate) {
                throw new RuntimeException("age不符合校验规则,应在(1-800)");
            }
    
            //......
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    二、javaEE规范的故事

    • 是什么?
      • 不相关的很多java package组成了javaee规范
    • 在哪里?
      • javax开头的包
      • javax.sql ---- mysql ,sqlserver,oracle …
      • Javax.jms ---- activemq
      • Javax.servlet ---- tomcat,jetty …
      • Javax.persistence ---- hibernate
      • Javax.transaction----分布式事务
      • javax.xml----jaxp: java api for xml processing

    ​ jdk自带了一些常用的javaee规范,对于没有自带的如果想要使用就需要自己引用了,比如beanvalidation

    • 如何制定?

      jcp官网:https://jcp.org/en/home/index

      Jsr:JavaSpecification Requests java规范提案, 如beanvalidation的提案有如下3个。

      • 由谁:jcp里的成员:https://jcp.org/en/participation/members
      提案号beanvalidation版本
      jsr303beanvalidation1.0
      jsr349beanvalidation1.1
      Jsr380beanvalidation2.0
    • javax走向jakarta

      参考:https://blogs.oracle.com/theaquarium/opening-up-java-ee oracle把javaee规范捐献给eclipse基金会

    • 不要和Apache的jakarta混为一谈

      2011退休了:https://jakarta.apache.org/

    三、非web环境下使用校验

    3.1 搭建环境

            
    
    
    
    
    
    
    
    
    
    
    
    
    
    
            
            <dependency>
                <groupId>org.hibernate.validatorgroupId>
                <artifactId>hibernate-validatorartifactId>
                <version>7.0.1.Finalversion>
            dependency>
            
            <dependency>
                <groupId>org.apache.tomcat.embedgroupId>
                <artifactId>tomcat-embed-elartifactId>
                <version>10.0.22version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3.2 validator初体验

    package com.zs.validation;
    
    import jakarta.validation.ConstraintViolation;
    import jakarta.validation.Validation;
    import jakarta.validation.Validator;
    
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    public class ValidationUtil {
    
        //线程安全的 http://www.360doc.com/content/16/0222/17/16926569_536478632.shtml
        private static Validator validator;
    
        static {
            validator = Validation.buildDefaultValidatorFactory().getValidator();
        }
    
        public static List<String>  valid(Object obj) {
            //如果被校验对象 没有校验通过,则set里面就有校验信息
            Set<ConstraintViolation<Object>> set = validator.validate(obj);
            List<String> list = set.stream().map(v -> "属性:" + v.getPropertyPath() + ",属性的值" +
                    v.getInvalidValue() + ",校验不通过的提示信息:" + v.getMessage())
                    .collect(Collectors.toList());
            return list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    添加注解

    import jakarta.validation.constraints.NotNull;
    
    @Data
    public class UserInfo {
        /**
         * 不能是null, "", "   "
         */
        @NotNull
        private String name;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试

        @Test
        public void test01(){
            UserInfo userInfo = new UserInfo();
            userInfo.setAge(2);
    //        userInfo.setName("zs");
    //        validateUserInfo(userInfo);
    
            List<String> list = ValidationUtil.valid(userInfo);
            System.out.println(list);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [属性:name,属性的值null,校验不通过的提示信息:不能为null]

    3.3 validato加载原理

    spi机制

    image-20220907002849439

    未命名绘图

    image-20220907004806321

    3.4 常用的校验约束注解

    1. Bean Validation中内置的 constraint
    @Null			被注释的元素必须为null
    @NotNull	被注释的元素必须不为null
    @NotEmpty	被注释的集合(size > 0)/字符串(!=null && !"")
    @NotBlank	!=null && !"" && !"   "
    
    @AssertTrue		被注释的元素必须为true
    @AssertFalse	被注释的元素必须为false
    @Min(value)		被注释的元素必须是一个数字,>=
    @Max(value)		被注释的元素必须是一个数字,<=
    @DecimalMin(value)		>=
    @DecimalMax(value)		<=
    
    @Size(max,min)		被注释的元素的大小必须在指定的范围内
    @Digits(integer,fraction)	被注释的元素必须是一个数字,其值必须在可接受的范围内
    @Past							被注释的元素必须是一个过去的日期
    @PastOrPresent		时间
    @NegativeOrZero		<=0
    @Future						被注释的元素必须是一个将来的日期
    @Pattern(value)		被注释的元素必须符合指定的正则表达式
    @Email						被注释的元素必须是电子邮箱地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    image-20220907010014875
    1. Hibernate Validator附加的 constraint: org.hibernate.validator.constraints
    @Length	被注释的字符串的大小必须在指定的范围内
    @Range	被注释的元素必须在适合的范围内
    @URL		一个url
    
    • 1
    • 2
    • 3
    image-20220907010315899
        /**
         * 不能是null, "", "   "
         */
        @NotBlank
    //    @NotNull
    //    @NotEmpty
        private String name;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
        /**
         * 正整数, 1-800
         */
    //    @Min(1) @Max(800)
         @Range(min=1,max=800)
        private Integer age;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    /**
     * email的格式
     */
    @NotBlank
    @Email
    private String email;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    /**
     * 符合中国大陆手机号
     */
    @Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$")
    private String phone;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    /**
     * 不能超过当前日期
     */
    @NotNull
    @Past
    private LocalDateTime birthday;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    /**
     * url
     */
    @URL
    private String personalPage;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    package com.zs.validation.bean;
    
    
    import jakarta.validation.constraints.*;
    import lombok.Data;
    import org.hibernate.validator.constraints.Range;
    import org.hibernate.validator.constraints.URL;
    
    import java.time.LocalDateTime;
    
    /**
     * 用户信息
     *
     * @author zs
     * @date 2022-09-06
     */
    @Data
    public class UserInfo {
        private Long id;
        /**
         * 不能是null, "", "   "
         */
        @NotBlank
    //    @NotNull
    //    @NotEmpty
        private String name;
        /**
         * 正整数, 1-800
         */
    //    @Min(1) @Max(800)
         @Range(min=1,max=800)
        private Integer age;
        /**
         * email的格式
         */
        @NotBlank
        @Email
        private String email;
        /**
         * 符合中国大陆手机号
         */
        @Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$")
        private String phone;
        /**
         * 不能超过当前日期
         */
        @NotNull
        @Past
        private LocalDateTime birthday;
        /**
         * url
         */
        @URL
        private String personalPage;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    测试

    package com.zs.validation;
    
    import com.zs.validation.bean.UserInfo;
    import org.junit.Test;
    
    import java.time.LocalDateTime;
    import java.util.List;
    
    public class TraditionalTest {
    
    
        @Test
        public void test01(){
            UserInfo userInfo = new UserInfo();
            userInfo.setAge(1);
            userInfo.setName("zs");
            userInfo.setEmail("aaaa@qq.com");
            userInfo.setPhone("13516466501");
            userInfo.setBirthday(LocalDateTime.now().minusDays(1));
            userInfo.setPersonalPage("http://www.baidu.com");
    //        validateUserInfo(userInfo);
    
            List<String> list = ValidationUtil.valid(userInfo);
            System.out.println(list);
        }
    
        private static void validateUserInfo(UserInfo userInfo){
            // 用户名校验
            String name = userInfo.getName();
            if (name == null || "".equals(name) || "".equals(name.trim())) {
                //不符合校验规则
                throw new RuntimeException("name 不符合校验规则");
            }
    
            // age校验
            Integer age = userInfo.getAge();
            boolean ageValidate = age > 1 && age < 800;
            if (!ageValidate) {
                throw new RuntimeException("age不符合校验规则,应在(1-800)");
            }
    
            //......
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    3.5 约束和校验类的绑定原理

    @NotNull

    NotNullValidator 类校验 NotNull注解

    同理 @Xxx注解的校验器类为XxxValidator

    org.hibernate.validator.internal.metadata.core.ConstraintHelper

    if (enabledBuiltinConstraints.contains(BuiltinConstraint.JAKARTA_VALIDATION_CONSTRAINTS_NOT_BLANK)) {
        putBuiltinConstraint(tmpConstraints, NotBlank.class, NotBlankValidator.class);
    }
    
    • 1
    • 2
    • 3
    private boolean isBuiltinConstraint(Class annotationType) {
        return BuiltinConstraint.isBuiltin(annotationType.getName());
    }
    
    • 1
    • 2
    • 3

    注意:一个注解约束可能对应多个约束Validator,如@NotEmpty

    image-20220907224014674

    3.6 自定义校验信息

    @NotBlank(message = "你的名字不能为空")
    private String name;
    
    @Min(value = 18,message = "你的名字小于{value},禁止进入")
    private Integer age;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public static List<String>  valid(Object obj) {
        //如果被校验对象 没有校验通过,则set里面就有校验信息
        Set<ConstraintViolation<Object>> set = validator.validate(obj);
        List<String> list = set.stream().map(v -> 
                        "属性:" + v.getPropertyPath() + 
                        ",属性的值" + v.getInvalidValue() + 
                        ",校验不通过的提示信息:" + v.getMessage() +
                        ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
        )
                .collect(Collectors.toList());
        return list;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.7 分组校验

    @Data
    public class UserInfo {
        public interface Add { }
        public interface Update { }
    
        // 默认的组: jakarta.validation.groups.Default
        @Null(groups = {Add.class, Default.class}) // 只用于新增
        @NotNull(groups = Update.class)// 用于修改
        private Long id;
    
        @NotBlank(message = "你的名字不能为空")
        private String name;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    package com.zs.validation;
    
    import jakarta.validation.ConstraintViolation;
    import jakarta.validation.Validation;
    import jakarta.validation.Validator;
    
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    public class ValidationUtil {
    
        //线程安全的 http://www.360doc.com/content/16/0222/17/16926569_536478632.shtml
        private static Validator validator;
    
        static {
            validator = Validation.buildDefaultValidatorFactory().getValidator();
        }
    
        public static List<String>  valid(Object obj,Class<?>... groups) {
            //如果被校验对象 没有校验通过,则set里面就有校验信息
            Set<ConstraintViolation<Object>> set = validator.validate(obj,groups);
            List<String> list = set.stream().map(v ->
                            "属性:" + v.getPropertyPath() +
                            ",属性的值" + v.getInvalidValue() +
                            ",校验不通过的提示信息:" + v.getMessage() +
                            ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
            )
                    .collect(Collectors.toList());
            return list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    测试

    @Test
    public void test01(){
        UserInfo userInfo = new UserInfo();
        userInfo.setAge(1);
        userInfo.setName("zs");
        userInfo.setEmail("aaaa@qq.com");
        userInfo.setPhone("13516466501");
        userInfo.setBirthday(LocalDateTime.now().minusDays(1));
        userInfo.setPersonalPage("http://www.baidu.com");
        
        List<String> list = ValidationUtil.valid(userInfo, UserInfo.Add.class, Default.class);
        System.out.println(list);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.8 @Valid级联校验

    import jakarta.validation.constraints.NotBlank;
    import lombok.Data;
    
    @Data
    public class Grade {
        
        //班级号
        @NotBlank
        private String no;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
        @NotNull
        @Valid //被引用对象加@Valid注解才可以完成级联校验
        private Grade grade;
    }
    
    • 1
    • 2
    • 3
    • 4
    userInfo.setGrade(new Grade());
    List<String> list = ValidationUtil.valid(userInfo, UserInfo.Add.class, Default.class);
    
    • 1
    • 2

    3.9 自定义校验规则

    注解

    package com.zs.validation.annotation;
    
    import jakarta.validation.Constraint;
    import jakarta.validation.Payload;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    @Documented
    @Constraint(validatedBy = {UserStatusValidator.class })//指定当前注解要被谁来完成校验工作
    @Target({FIELD})
    @Retention(RUNTIME)
    public @interface UserStatus {
    
        String message() default "{userStatus必须是范围内的值}";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注解校验器

    public class UserStatusValidator implements ConstraintValidator<UserStatus, String> {
    
        @Override
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            if (s == null) {
                return true;
            } else {
                Set<String> set = new HashSet<>();
                set.add("10");
                set.add("20");
                set.add("30");
                return set.contains(s);
            }
        }
    
        @Override
        public void initialize(UserStatus constraintAnnotation) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    使用

    @UserStatus
    private String status;
    
    • 1
    • 2

    测试

        @Test
        public void test01(){
            UserInfo userInfo = new UserInfo();
            userInfo.setAge(19);
            userInfo.setName("zs");
            userInfo.setEmail("aaaa@qq.com");
            userInfo.setPhone("13516466501");
            userInfo.setBirthday(LocalDateTime.now().minusDays(1));
            userInfo.setPersonalPage("http://www.baidu.com");
            userInfo.setGrade(new Grade().setNo("11"));
            userInfo.setStatus("10");
            List<String> list = ValidationUtil.valid(userInfo, UserInfo.Add.class, Default.class);
            System.out.println(list);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.10 快速校验failfast

    package com.zs.validation;
    
    import jakarta.validation.ConstraintViolation;
    import jakarta.validation.Validation;
    import jakarta.validation.Validator;
    import org.hibernate.validator.HibernateValidator;
    
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    public class ValidationUtil {
    
        //线程安全的 http://www.360doc.com/content/16/0222/17/16926569_536478632.shtml
        private static Validator validator;
        private static Validator failFastValidator;
    
        static {
            //默认校验器
            validator = Validation.buildDefaultValidatorFactory().getValidator();
            //快速失败的校验器
            failFastValidator = Validation.byProvider(HibernateValidator.class)
                    .configure().failFast(true) //配置快速失败
                    .buildValidatorFactory().getValidator();
        }
    
        public static List<String>  valid(Object obj,Class<?>... groups) {
            //如果被校验对象 没有校验通过,则set里面就有校验信息
            Set<ConstraintViolation<Object>> set = validator.validate(obj,groups);
            List<String> list = set.stream().map(v ->
                            "属性:" + v.getPropertyPath() +
                            ",属性的值" + v.getInvalidValue() +
                            ",校验不通过的提示信息:" + v.getMessage() +
                            ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
            )
                    .collect(Collectors.toList());
            return list;
        }
    
    
        public static List<String>  validFailFast(Object obj,Class<?>... groups) {
            //如果被校验对象 没有校验通过,则set里面就有校验信息
            Set<ConstraintViolation<Object>> set = failFastValidator.validate(obj,groups);
            List<String> list = set.stream().map(v ->
                    "属性:" + v.getPropertyPath() +
                            ",属性的值" + v.getInvalidValue() +
                            ",校验不通过的提示信息:" + v.getMessage() +
                            ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
            )
                    .collect(Collectors.toList());
            return list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    3.10 非bean入参校验

    package com.zs.validation;
    
    import jakarta.validation.ConstraintViolation;
    import jakarta.validation.Validation;
    import jakarta.validation.Validator;
    import jakarta.validation.executable.ExecutableValidator;
    import org.hibernate.validator.HibernateValidator;
    
    import java.lang.reflect.Method;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    public class ValidationUtil {
    
        //线程安全的 http://www.360doc.com/content/16/0222/17/16926569_536478632.shtml
        private static Validator validator;
        private static Validator failFastValidator;
        private static ExecutableValidator executableValidator;
    
        static {
            //默认校验器
            validator = Validation.buildDefaultValidatorFactory().getValidator();
    
            //快速失败的校验器
            failFastValidator = Validation.byProvider(HibernateValidator.class)
                    .configure().failFast(true) //配置快速失败
                    .buildValidatorFactory().getValidator();
    
            //校验入参或返回值
            executableValidator = validator.forExecutables();
        }
    
        public static List<String>  valid(Object obj,Class<?>... groups) {
            //如果被校验对象 没有校验通过,则set里面就有校验信息
            Set<ConstraintViolation<Object>> set = validator.validate(obj,groups);
            List<String> list = set.stream().map(v ->
                            "属性:" + v.getPropertyPath() +
                            ",属性的值" + v.getInvalidValue() +
                            ",校验不通过的提示信息:" + v.getMessage() +
                            ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
            )
                    .collect(Collectors.toList());
            return list;
        }
    
    
        public static List<String>  validFailFast(Object obj,Class<?>... groups) {
            //如果被校验对象 没有校验通过,则set里面就有校验信息
            Set<ConstraintViolation<Object>> set = failFastValidator.validate(obj,groups);
            List<String> list = set.stream().map(v ->
                    "属性:" + v.getPropertyPath() +
                            ",属性的值" + v.getInvalidValue() +
                            ",校验不通过的提示信息:" + v.getMessage() +
                            ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
            )
                    .collect(Collectors.toList());
            return list;
        }
    
    
        /**
         * 非bean参数校验
         *
         * @param object          对象
         * @param method          方法
         * @param parameterValues 参数值
         * @param groups          组
         * @return {@link List}<{@link String}>
         */
        public static <T> List<String> validNotBean(T object, Method method, Object[] parameterValues, Class<?>... groups) {
            Set<ConstraintViolation<Object>> set = executableValidator.validateParameters(object,method,parameterValues,groups);
            
            List<String> list = set.stream().map(v ->
                    "属性:" + v.getPropertyPath() +
                            ",属性的值" + v.getInvalidValue() +
                            ",校验不通过的提示信息:" + v.getMessage() +
                            ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
            )
                    .collect(Collectors.toList());
            return list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    /**
     * 方法非bean类型的入参校验
     * 1. 方法参数前加注释
     * 2. 执行入参校验,真正要用的话可以使用AOP编程来使用,web环境是spring已经做了
     *
     * @param name 名字
     * @return {@link String}
     */
    public String getByName(@NotNull String name) {
    
        StackTraceElement st = Thread.currentThread().getStackTrace()[1];
        String methodName = st.getMethodName();
        Method method = null;
        try {
            method = this.getClass().getDeclaredMethod(methodName,String.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        List<String> strings = ValidationUtil.validNotBean(this, method, new Object[]{name});
        System.out.println(strings);
        return "ok";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    @Test
    public void test02(){
        UserInfoService userInfoService = new UserInfoService();
        String byName = userInfoService.getByName(null);
        System.out.println(byName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    四、web环境中使用

    4.1 搭建springboot环境

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-dependenciesartifactId>
          <version>2.5.0version>
          <scope>importscope>
          <type>pomtype>
        dependency>
      dependencies>
    dependencyManagement>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-validationartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class UserInfoHandler {
    
        @GetMapping("/getByName")
        public String getByName(String name){
            return name + "ok";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    package com.zs;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class App {
        public static void main(String[] args) {
            SpringApplication.run(App.class);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4.2 使用@Valid自动校验

    /**
     * 编程式校验
     *
     * @param userInfo 用户信息
     * @return {@link String}
     */
    @GetMapping("/addUser")
    public String addUser(UserInfo userInfo){
        List<String> result = ValidationUtil.valid(userInfo);
        if (result.size() > 0) {
            return "failed";
        } else {
            return "success";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
        @GetMapping("/addUser2")
        public String addUser2(@Valid UserInfo userInfo, BindingResult bindingResult){
            if (bindingResult.hasErrors()) { //判断是不是满足约束
                List<ObjectError> allErrors = bindingResult.getAllErrors();
                for (ObjectError error : allErrors) {
                    System.out.println(error.getObjectName() + "::" + error.getDefaultMessage());
                }
    
                //获取没通过校验的字段详情
                List<FieldError> fieldErrors = bindingResult.getFieldErrors();
                for (FieldError fieldError : fieldErrors) {
                    System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage()
                     + ",当前没通过校验规则的值是:" + fieldError.getRejectedValue());
                }
            }
            return "ok";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    userInfo::你的名字不能为空
    userInfo::不能为null
    userInfo::不能为空
    userInfo::不能为null
    name:你的名字不能为空,当前没通过校验规则的值是:null
    birthday:不能为null,当前没通过校验规则的值是:null
    email:不能为空,当前没通过校验规则的值是:null
    grade:不能为null,当前没通过校验规则的值是:null

    4.3 使用@Validated自动校验 (指定分组)

    /**
     * 编程式校验
     *
     * @param userInfo 用户信息
     * @return {@link String}
     */
    @GetMapping("/addUser3")
    public String addUser3(@Validated(value={UserInfo.Add.class, Default.class}) UserInfo userInfo, BindingResult bindingResult){
        if (bindingResult.hasErrors()) { //判断是不是满足约束
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError error : allErrors) {
                System.out.println(error.getObjectName() + "::" + error.getDefaultMessage());
            }
    
            //获取没通过校验的字段详情
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage()
                        + ",当前没通过校验规则的值是:" + fieldError.getRejectedValue());
            }
        }
        return "ok";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4.4 统一异常处理

    @GetMapping("/addUser4")
    public String addUser4(@Validated(value={UserInfo.Add.class, Default.class}) UserInfo userInfo){
        return "ok";
    }
    
    @ExceptionHandler(BindException.class)
    public String handleEx(BindException e) {
      List<FieldError> fieldErrors = e.getFieldErrors();
      StringBuilder stringBuilder = new StringBuilder();
      for (FieldError fe : fieldErrors) {
        stringBuilder.append("属性:").append(fe.getField())
          .append("校验不通过,原因:").append(fe.getDefaultMessage())
          .append(";");
      }
      return stringBuilder.toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.5 区别@Validated 和 @Valid

    • @Validated 可以指定分组

    • @Validated 支持方法参数的自动校验

    @RestController
    @Validated //表示整个类都启用校验,如果碰到入参含有bean validation 注解的话,就会自动校验
    public class UserInfoHandler {
    
        @GetMapping("/getByName")
        public String getByName(@NotNull String name){
            return name + "ok";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //@Validated 注解写在方法上的时候报的错误
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public String handleEx(BindException e) {
        List<FieldError> fieldErrors = e.getFieldErrors();
        StringBuilder stringBuilder = new StringBuilder();
        for (FieldError fe : fieldErrors) {
            stringBuilder.append("属性:").append(fe.getField())
                    .append("校验不通过,原因:").append(fe.getDefaultMessage())
                    .append(";");
        }
        return stringBuilder.toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    //@Validated 注解写在类上报的异常
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public  List<String> handleEx(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> set = e.getConstraintViolations();
        List<String> list = set.stream().map(v ->
                        "属性:" + v.getPropertyPath() +
                                ",属性的值" + v.getInvalidValue() +
                                ",校验不通过的提示信息:" + v.getMessage() +
                                ",消息模板(为被替换的提示信息):" + v.getMessageTemplate()
        )
                .collect(Collectors.toList());
        return list;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    示例一

    一、背景

    在某些项目场景中,需要使用代码校验实体类的参数值是否符合需求,并且返回值是动态的情况下,此时需要校验工具类来实现此功能。

    二、hibernate-validator

    1.maven

            
                org.hibernate
                hibernate-validator
                6.0.16.Final
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.校验工具类

    package com.asyf.demo.other_api.hibernatevalidator;
    
    import cn.hutool.json.JSONUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.hibernate.validator.HibernateValidator;
    
    import javax.validation.ConstraintViolation;
    import javax.validation.Validation;
    import javax.validation.Validator;
    import javax.validation.ValidatorFactory;
    import javax.validation.groups.Default;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @Description 实体校验工具类
     */
    @Slf4j
    public class ValidateUtil {
    
        /**
         * 验证器
         */
        private static Validator validator;
    
        static {
    //        validator = Validation.buildDefaultValidatorFactory().getValidator();
            ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                    .configure()
                    // 快速失败模式
                    .failFast(false)
                    .buildValidatorFactory();
            validator = validatorFactory.getValidator();
        }
    
    
        /**
         * 校验实体,返回实体所有属性的校验结果
         *
         * @param obj
         * @param 
         * @return
         */
        public static <T> ValidationResult validateEntity(T obj) {
            //解析校验结果
            Set<ConstraintViolation<T>> validateSet = validator.validate(obj, Default.class);
            return buildValidationResult(validateSet);
        }
    
        /**
         * 校验指定实体的指定属性是否存在异常
         *
         * @param obj
         * @param propertyName
         * @param 
         * @return
         */
        public static <T> ValidationResult validateProperty(T obj, String propertyName) {
            Set<ConstraintViolation<T>> validateSet = validator.validateProperty(obj, propertyName, Default.class);
            return buildValidationResult(validateSet);
        }
    
        /**
         * 将异常结果封装返回
         *
         * @param validateSet
         * @param 
         * @return
         */
        private static <T> ValidationResult buildValidationResult(Set<ConstraintViolation<T>> validateSet) {
            ValidationResult validationResult = new ValidationResult();
            if (!validateSet.isEmpty()) {
                validationResult.setHasErrors(true);
                Map<String, String> errorMsgMap = new HashMap<>();
                for (ConstraintViolation<T> constraintViolation : validateSet) {
                    errorMsgMap.put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage());
                }
                validationResult.setErrorMsg(errorMsgMap);
            }
            return validationResult;
        }
    
        public static void main(String[] args) {
            User user = new User();
            ValidationResult validationResult = ValidateUtil.validateEntity(user);
            log.info(JSONUtil.toJsonStr(validationResult));
            //og.info(validationResult.getMessage());
        }
    
    }
    package com.asyf.demo.other_api.hibernatevalidator;
    
    import lombok.Data;
    import org.apache.commons.lang3.StringUtils;
    
    import java.text.MessageFormat;
    import java.util.Map;
    
    @Data
    public class ValidationResult {
        /**
         * 是否有异常
         */
        private boolean hasErrors;
    
        /**
         * 异常消息记录
         */
        private Map<String, String> errorMsg;
    
        /**
         * 获取异常消息组装
         *
         * @return
         */
        public String getMessage() {
            if (errorMsg == null || errorMsg.isEmpty()) {
                return StringUtils.EMPTY;
            }
            StringBuilder message = new StringBuilder();
            errorMsg.forEach((key, value) -> {
                message.append(MessageFormat.format("{0}:{1} \r\n", key, value));
            });
            return message.toString();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128

    3.自定义校验注解

    package com.asyf.demo.other_api.hibernatevalidator;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = MyConstraintValidator.class)
    public @interface MyValidatorAnnotation {
    
        String message() default "性别有误";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        String type() default "abc";
    
    }
    package com.asyf.demo.other_api.hibernatevalidator;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    @Slf4j
    public class MyConstraintValidator implements ConstraintValidator<MyValidatorAnnotation, String> {
    
        private MyValidatorAnnotation annotation;
    
        public MyConstraintValidator() {
            //每添加一次注解会实例化一个对象
            //log.info("构造函数");
        }
    
        @Override
        public void initialize(MyValidatorAnnotation annotation) {
            //log.info("初始化");
            this.annotation = annotation;
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            log.info("校验 type:{}", annotation.type());
            //禁用默认的message的值
            context.disableDefaultConstraintViolation();
            //重新添加错误提示语句
            context.buildConstraintViolationWithTemplate("重新添加错误提示语句").addConstraintViolation();
            //返回校验结果
            return "正确的值".equals(value);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    4.校验实体类

    package com.asyf.demo.other_api.hibernatevalidator;
    
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import lombok.Data;
    
    import javax.validation.constraints.NotNull;
    
    @Data
    public class User {
    
        //    @NotBlank
        @MyValidatorAnnotation(type = "1", message = "name错误")
        private String name;
    
        @NotNull
        @JsonIgnore
        private Integer age;
    
        @MyValidatorAnnotation(type = "2", message = "email错误")
        private String email;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5.main函数测试

    public static void main(String[] args) {
            User user = new User();
            ValidationResult validationResult = ValidateUtil.validateEntity(user);
            log.info(JSONUtil.toJsonStr(validationResult));
            //og.info(validationResult.getMessage());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三、测试结果

     INFO [main] - 校验 type:2
     INFO [main] - 校验 type:1
     INFO [main] - {"hasErrors":true,"errorMsg":{"name":"重新添加错误提示语句","email":"重新添
    
    • 1
    • 2
    • 3

    https://www.bilibili.com/video/BV1UE411t7BZ?spm_id_from=333.337.search-card.all.click&vd_source=746baba3d7924e4ac64a8318afa1567f

  • 相关阅读:
    SQL sever中的索引
    Java序列化和Json格式的转化
    c++ Reference Collapsing
    java CharArrayReader类、CharArrayWriter类
    Taurus .Net Core 微服务开源框架:Admin 插件【1】 - 微服务节点管理
    深度学习_目标检测_SPP(Spatial Pyramid Pooling)详解
    信息抽取/实体关系抽取之UIE
    BI系统打包Docker镜像及部署的技术难度和实现
    redis入门
    View菜单解析
  • 原文地址:https://blog.csdn.net/weixin_44235759/article/details/126756853