美文网首页程序员
Bean Validation和Hibernate Valida

Bean Validation和Hibernate Valida

作者: 公子拙 | 来源:发表于2018-07-11 13:02 被阅读0次

    概述

    Bean Validation是用于数据校验的一套规范,
    而Hibernate Validator是此规范的参考实现且对其进行了扩展。

    几个概念

    • Bean
      符号java bean规范定义的java类的对象
    • 约束:
      验证条件,如非null,非空等,一般用注解定义
    • 校验器:
      对bean进行验证的对象,规范中定义为Validator/ExecutableValidator
    • 约束违反(ConstraintViolation):
      校验器按照定义的约束去验证某个值的结果,称为约束违反
    • 消息绑定文件
      用于提供约束违反消息描述的资源文件,默认文件名为:ValidationMessages.properties

    核心API

    核心包为javax.validation,核心对象为ValidatorExecutableValidator

    其中Validator主要用于验证bean对象,常用方法如下:

    • Set<ConstraintViolation> validateValue(bean,property,value,groups)
      验证值value是否违反对象bean中的property属性上定义的约束
    • Set<ConstraintViolation> validateProperty(bean,property,groups)
      验证值对象bean中的property属性的值是否违反其上定义的约束
    • Set<ConstraintViolation> validate(bean,groups)
      验证值对象bean中的所有属性的值是否违反各自属性定义的约束

    ExecutableValidator主要用于在方法执行时验证方法参数和构造器参数

    • Set<ConstraintViolation> validateConstructorParameters(constructor,paramValues,groups)
      验证构造器constructor执行的参数paramValues

    • Set<ConstraintViolation> validateConstructorReturnValue(constructor,createdObject,groups)
      验证构造器constructor构造的对象是否符合约束

    • Set<ConstraintViolation> validateParameters(object,method,paramValues,groups)
      验证对象object中的method方法执行的参数paramValues

    • Set<ConstraintViolation> validateReturnValue(object,method,returnValue,groups)
      验证对象object中的method方法的返回值

    其中ConstraintViolation表示验证结果。

    常用约束

    • @NotNull
      不为 null,可用于任意类型
    • @NotBlank
      不为 null且trim后长度不为0,仅用于CharSequence
    • @NotEmpty
      不为null或者为空,可用于CharSequence/Collection/Map/数组
    • @Size(min,max)
      验证字符串的长度在min和max之间,包括min,max

    其他请参见后文的约束列表

    使用步骤

    1. 配置依赖
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>版本</version>
    </dependency>
    <!--还依赖于EL,如果是Web环境,则由容器提供-->
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b09</version>
        <scope>provided</scope>
    </dependency>
    
    1. 编写要进行验证的bean
    public class Student {
        @NotEmpty
        private String no;
    
        @NotEmpty
        @Size(min = 2, max = 14)
        private String name;
    
        @NotNull
        @Min(18)
        private int age;
    
        //getters and setters ...
    }
    
    
    1. 获取验证器
     ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
     Validator validator = factory.getValidator();
    
    1. 创建bean对象,进行验证,获取验证结果
    Student student=new Student();
    student.setNo("10001");
    student.setName("张三");
    student.setAge(15);
    
    Set<ConstraintViolation<Student>> violations =
                    validator.validate( student );
    for(ConstraintViolation v:violations){
        System.out.println(v.getMessage());
    }
    

    自定义验证消息

    public class Student {
    
        @NotNull(message = "学号不能为空")
        @Size(min = 2,
              max = 14,
              message = "学号[${validatedValue}]长度为{min}到 {max}"
              )
        private String no;
       
    
        //constructor, getters and setters ...
    }
    
    

    消息参数和消息表达式

    被{}包围的成为消息参数
    被${}包围的称为消息表达式

    消息参数解析规则如下:

    1. 所有的消息参数首先从自定义消息文件(ValidationMessages)从获取,如果未找到,执行步骤2

    2. 所有消息参数再从内置消息文件中获取(org.hibernate.validator.ValidationMessages),如果未找到,执行步骤3

    3. 所有消息参数再去约束注解进行注解属性匹配,匹配上,则取对应属性的值,如果还未找到,则原样输出。

    4. 消息表达式规则:
      消息表达式写法类似EL表达式。可用上下文如下:

      • 约束注解属性值,用法:${属性名}
      • 当前验证值,使用${validatedValue>1}
      • 格式化器,用法:${formatter.format('%1$.2f',validatedValue)},相当于调用java.util.Formatter.format(format,...args);

    特殊字符

    • \{={
    • \}=}
    • \=
    • \\=\

    约束组

    每个约束都至少要属于一个组,没有指定则属于默认(javax.validation.groups.Default)组。如果指定则不再属于默认组。

    把约束分组可以让我们在对bean进行验证时可以更灵活。
    比如:同样一个bean,在新增和修改两个业务场景中需要验证的属性是不一样的(如新增不需要验证id,修改时则需要)

    步骤:
    1.定义约束组
    任意定义一个接口即可

    public interface AddGroup{
    }
    public interface ModGroup{
    }
    

    2.在注解中声明

    public class Student {
        @NotNull(groups=Mod.class)
        public String no;
        @NotEmpty(groups=Add.class,Mod.class)
        publilc String name;
        
    }
    

    3.验证时使用

    validator.validate(student,Add.class);
    validator.validate(student,Add.class,Mod.class);
    

    注:如果验证时未传递组类型,则使用默认组

    自定义约束

    两个步骤,如编写一个验证字符串必须全是大写的验证器

    1.创建约束注解

    @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE })
    @Retention(RUNTIME)
    @Constraint(validatedBy = MyUpperCaseValidator.class)
    public @interface UpperCase {
        String message() default "{must-upper}";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    
    }
    

    2.编写验证器(Implement ConstraintValidator)

    public class MyUpperCaseValidator implements ConstraintValidator<UpperCase, String> {
    
        @Override
        public void initialize(UpperCase constraintAnnotation) {
            
        }
    
        @Override
        public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
            if ( object == null ) {
                return true;
            }
            return object.equals( object.toUpperCase() );
        }
    }
    

    两种验证模式

    • 快速失败(fail fast)
      当发生第一个验证失败时就立即结束。

    设置快速失败模式,如下:

        validator = Validation.byProvider(HibernateValidator.class )
                    .configure()
                    .failFast( true )
                    .buildValidatorFactory()
                    .getValidator();
    
    • 非快速失败模式(默认)
      收集所有失败再结束

    spring集成

    1.注册验证器和方法验证

    <bean id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
    
    <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
    
    

    2.使用验证器

    @Service
    public class MyService {
    
        @Autowired
        private Validator validator;
    
    }
    

    3.进行方法验证
    在方法所在类上注解@Validated,以提供AOP切点

    @Validated
    public class XXXService{
        public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2){
        ...
    }
    }
    

    如果方法参数或返回值验证失败,则方法抛出ConstraintViolationException异常。可通过异常来获取验证失败消息。

    spring mvc集成

    如果Bean Validation jar包在类路径中存在,则spring会默认注册。
    这可以直接在Controller中对请求参数bean进行验证

    @Controller
    public class UserController{
        @GetMapping("/user/add.do")
        public String addUser(@Validated UserBean bean){
            ...
        }
        @GetMapping("/user/mod.do")
        public String modUser(@Validated UserBean bean,Errors es){
            if(es.hasErrors){
                //获得验证异常消息
            }
            .
        }
    }
    

    如果需要在方法中处理异常,则可以添加Errors参数,用于获取验证异常消息
    否则,验证失败,方法抛出ConstraintViolationException异常。

    非bean参数验证

    上面的验证仅能针对bean生效,如果要验证非bean参数,写法同spring集成。
    如业务层参数验证:

    @Service
    @Validated
    public class UserService {
        public @NotNull Object login(@NotEmpty String account, @NotEmpty String password){
            ...
        }
    }
    

    login方法会抛出ConstraintViolationException异常,可以表现层中统一处理。

    <h1 id="constrain-list">验证约束列表</h1>

    表 1. Bean Validation 中内置的 constraint

    • @Null
      被注释的元素必须为 null,可用于任意类型

    • @NotNull
      被注释的元素必须不为 null,可用于任意类型

    • @NotBlank
      被注释的元素必须不为 null且trim后长度不为0,仅用于CharSequence

    • @NotEmpty
      被注释的元素不可为null或者为空,可用于CharSequence/Collection/Map/数组

    • @AssertTrue
      被注释的元素必须为 true,用于boolean/Boolean字段

    • @AssertFalse
      被注释的元素必须为 false,用于boolean/Boolean字段

    • @Min(value)
      被注释的元素必须是一个数字,其值必须大于等于指定的最小值,可用于:于BigDecimal/BigInteger/byte, short, int, long 和对应包装类

    注:不能用于double和float(因为四舍五入问题)

    • @Max(value)
      被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    • @DecimalMin(value)
      和@Min含义一样,但可用于CharSequence(字符串)
    • @DecimalMax(value)
      和@Max含义一样,但可用于CharSequence(字符串)
    • @Size(max, min)
      被注释的元素的大小必须在指定的范围内,可用于String/Collection/Map/Array
    • @Digits (integer, fraction) 被注释的元素必须是一个数字,其整数部分位数不能超过integer个,小数部分位数不能超过fraction个,可用于:同DecimalMax
    • @Positive
      被注释的元素必须为正数,可用于:同DecimalMax
    • @PositiveOrZero
      被注释的元素必须为正数或0,可用于:同DecimalMax
    • @Negative
      被注释的元素必须为负数,可用于:同DecimalMax
    • @NegativeOrZero
      被注释的元素必须为负数或0,可用于:同DecimalMax
    • @Past
      被注释的元素必须是一个过去的日期,可用于:所有日期相关类
    • @PastOrPresent
      被注释的元素必须是一个过去的日期或当下,可用于:所有日期相关类
    • @Future
      被注释的元素必须是一个将来的日期,可用于:所有日期相关类
    • @FutureOrPresent
      被注释的元素必须是当下或者将来的日期,可用于:所有日期相关类
    • @Pattern(regex=, flags=)
      被注释的元素必须符合指定的正则表达式,仅用于String
    • @Email
      被注释的元素必须是电子邮箱地址,仅用于String
    • @NotEmpty
      被注释的字符串的必须非空,仅用于String
    • @Range
      被注释的元素必须在合适的范围内

    表 2. Hiberate Validator 增加的 constraint

    • @CreditCardNumber(ignoreNonDigitCharacters=)
      被注释的字符串必须是一个有效的信用卡号

    • @EAN
      被注释的字符串必须是一个有效的EAN码(国际物品编码协会制定的一种商品用条码)

    • @URL(protocol=, host=, port=, regexp=, flags=)

    • @ISBN
      被注释的字符串必须是一个有效的ISBN(国际标准图书号)

    • @UniqueElements
      被注释的集合中仅能包含唯一性元素

    • @Length
      被注释的字符串的大小必须在指定的范围内,仅用于String

    • @Range(min=, max=)
      被注释的元素的值是否在min和max范围内(包含min和max),用于BigDecimal/BigInteger/CharSequence/byte, short, int, long及其包装类。

    其他请参见hiernate官方文档

    相关文章

      网友评论

        本文标题:Bean Validation和Hibernate Valida

        本文链接:https://www.haomeiwen.com/subject/ztlhpftx.html