概述 Bean Validation 规范
Bean 是 Java Bean 的缩写。在 Java 分层架构的实际应用中,从表示层到持久化层。每一层都须要对 Java Bean 进行业务符合性验证,如图 1 所看到的。然而对于同一个 Java Bean 的对象,在每一层都须要实现相同的验证逻辑时,这将是一项耗时且easy诱发错误的做法。
Bean Validation 规范的目标就是避免多层验证的反复性。
其实。开发人员更倾向于将验证规则直接放到 Java Bean 本身,使用注解的方式进行验证规则的设计。
图 1. Java 分层验证结构示意图
JSR303 规范(Bean Validation 规范)提供了对 Java EE 和 Java SE 中的 Java Bean 进行验证的方式。该规范主要使用注解的方式来实现对 Java Bean 的验证功能,而且这样的方式会覆盖使用 XML 形式的验证描写叙述符,从而使验证逻辑从业务代码中分离出来,如图 2 所看到的。
图 2. Java Bean 验证模型示意图
JSR303 规范提供的 API 是 Java Bean 对象模型的一般扩展,它并不局限于某一层或者某一编程模型,在server端和client都可使用,其最大的特点就是易用并且灵活。
Hibernate Validator 4.0 是 JSR303 规范的參考实现,本文全部演示样例代码均使用该參考实现。
以下给出一个 Bean Validation 的简单演示样例(清单 1):
清单 1:
public class Employee {
@NotNull(message = "The id of employee can not be null")
private Integer id; @NotNull(message = "The name of employee can not be null")
@Size(min = 1,max = 10,message="The size of employee's name must between 1 and 10")
private String name; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Employee employee = new Employee();
employee.setName("Zhang Guan Nan");
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<Employee>> set = validator.validate(employee);
for (ConstraintViolation<Employee> constraintViolation : set) {
System.out.println(constraintViolation.getMessage());
}
}
}
执行该演示样例的输出结果为:
The size of employee's name must between 1 and 10
The id of employee can not be null
从演示样例中能够看出,Bean Validation 使用注解(@NotNull 和 @Size)的方式对字段 id 和 name 进行了约束声明,当该 Java Bean 被实际使用时,相关的验证器就会对该类的实例进行验证确保其符合该约束声明。
完毕 Java Bean 的验证通常可分为例如以下四个步骤:
- 约束注解的定义
- 约束验证规则(约束验证器)
- 约束注解的声明
- 约束验证流程
本文第二大部分将具体介绍约束注解的定义和约束验证规则;第三大部分将具体介绍约束注解的声明和约束验证流程;第四大部分将介绍 JSR303 规范提供的 API。
约束的定义
约束注解
Bean Validation 规范对约束的定义包含两部分。一是约束注解,清单 1 中的 @NotNull 就是约束注解。二是约束验证器,每个约束注解都存在相应的约束验证器。约束验证器用来验证详细的 Java Bean 是否满足该约束注解声明的条件。
在 Java Bean 中,对某一方法、字段、属性或其组合形式等进行约束的注解,即为约束注解,如清单 2 所看到的:
清单 2:
@NotNull(message = "The id of employee can not be null")
private Integer id;
清单 2 的含义为:对于字段 id。在 Java Bean 的实例中值不能为空。对于每个约束注解,在实际使用前必须有相关定义。JSR303 规范默认提供了几种约束注解的定义(见表 1),我们也能够扩展规范提供的 API。实现符合自身业务需求的约束注解。
表 1. Bean Validation 规范内嵌的约束注解定义
约束注解名称 | 约束注讲解明 |
---|---|
@Null | 验证对象是否为空 |
@NotNull | 验证对象是否为非空 |
@AssertTrue | 验证 Boolean 对象是否为 true |
@AssertFalse | 验证 Boolean 对象是否为 false |
@Min | 验证 Number 和 String 对象是否大等于指定的值 |
@Max | 验证 Number 和 String 对象是否小等于指定的值 |
@DecimalMin | 验证 Number 和 String 对象是否大等于指定的值,小数存在精度 |
@DecimalMax | 验证 Number 和 String 对象是否小等于指定的值,小数存在精度 |
@Size | 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 |
@Digits | 验证 Number 和 String 的构成是否合法 |
@Past | 验证 Date 和 Calendar 对象是否在当前时间之前 |
@Future | 验证 Date 和 Calendar 对象是否在当前时间之后 |
@Pattern | 验证 String 对象是否符合正則表達式的规则 |
约束注解和普通的注解一样。一个典型的约束注解的定义应该至少包含例如以下内容(清单 3):
清单 3:
@Target({ }) // 约束注解应用的目标元素类型
@Retention() // 约束注解应用的时机
@Constraint(validatedBy ={}) // 与约束注解关联的验证器
public @interface ConstraintName{
String message() default " "; // 约束注解验证时的输出消息
Class<?>[] groups() default { }; // 约束注解在验证时所属的组别
Class<? extends Payload>[] payload() default { }; // 约束注解的有效负载
}
约束注解应用的目标元素类型包含 METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER。METHOD 约束相关的 getter 方法;FIELD 约束相关的属性。TYPE 约束详细的 Java Bean;ANNOTATION_TYPE 用在组合约束中;该规范相同也支持对參数(PARAMETER)和构造器(CONSTRUCTOR)的约束。
验证时的组别属性将在本文第三大部分中组与组序列中具体介绍。
有效负载通经常使用来将一些元数据信息与该约束注解相关联,经常使用的一种情况是用负载表示验证结果的严重程度。
清单 4 给出一个验证字符串非空的约束注解的定义:
清单 4:
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {NotEmptyValidator.class})
public @interface NotEmpty {
String message() default "this string may be empty";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
约束注解定义完毕后。须要同一时候实现与该约束注解关联的验证器。约束验证器的实现须要扩展 JSR303 规范提供的接口 javax.validation.ConstraintValidator。清单 5 给出该接口。
清单 5:
public interface ConstraintValidator<A extends Annotation, T> {
void initialize(A constraintAnnotation);
boolean isValid(T value, ConstraintValidatorContext context);
}
该接口有两个方法,方法 initialize 对验证器进行实例化,它必须在验证器的实例在使用之前被调用,并保证正确初始化验证器。它的參数是约束注解。方法 isValid 是进行约束验证的主体方法,当中 value 參数代表须要验证的实例,context 參数代表约束运行的上下文环境。
对于清单 4 定义的约束注解,清单 6 给出了与该注解相应的验证器的实现。
清单 6:
public class NotEmptyValidator implements ConstraintValidator<NotEmpty, String>{
public void initialize(NotEmpty parameters) {
}
public boolean isValid(String string,
ConstraintValidatorContext constraintValidatorContext) {
if (string == null) return false;
else if(string.length()<1) return false;
else return true;
}
}
至此,一个能够声明并使用的约束注解已经定义完成,清单 7 将给出该约束注解在实际程序中的使用。为节省篇幅。这里仅仅给出针对清单 1 的添加和改动内容,未给出所有的演示样例代码,您能够在本文的附录中获得所有的代码。
清单 7:
首先在清单 1 中的类 Employee 中增加字段 company 和对应的 getter 和 setter 方法:
@NotEmpty
private String company;
然后在 main 函数中增加例如以下代码清单:
String company = new String();
employee.setCompany(company);
再次执行该程序。输出结果为:
The id of employee can not be null
this string may be empty
The size of employee's name must between 1 and 10
多值约束
以下介绍 Bean Validation 规范的一个特性。多值约束(Multiple Constraints):对于同一个目标元素,在进行约束注解声明时能够同一时候使用不同的属性达到对该目标元素进行多值验证的目的。如清单 8 所看到的:
清单 8:
public @interface ConstraintName{
String message() default " ";
Class<? >[] groups() default { };
Class<? extends Payload>[] payload() default { };
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
ConstraintName[] value();
}
}
实现多值约束仅仅须要在定义约束注解的同一时候定义一个 List(@interface List{})。使用该约束注解时。Bean Validation 将 value 数组里面的每个元素都处理为一个普通的约束注解,并对其进行验证,全部约束条件均符合时才会验证通过。
清单 9 定义了一个约束注解。它用来验证某一字符串是否包括指定的内容。
清单 9:
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = PatternOfStringValidator.class)
public @interface PatternOfString {
String mustContainLetter();
String message() default "this pattern may not be right";
Class<? >[] groups() default { };
Class<? extends Payload>[] payload() default {}; @Target({ METHOD, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
@interface List {
PatternOfString[] value();
}
}
该约束注解相应的验证器如清单 10 所看到的:
清单 10:
public class PatternOfStringValidator implements ConstraintValidator
<PatternOfString, String> {
private String letterIn;
public void initialize(PatternOfString parameters) {
this.letterIn=parameters.mustContainLetter();
}
public boolean isValid(String string,
ConstraintValidatorContext constraintValidatorContext) {
if (string.contains(letterIn))
return true;
return false;
}
}
假设想验证某一字符串是否同一时候包括两个子串,那么多值约束就显得比較重要了,清单 11 将具体给出多值约束的使用。
清单 11:
在清单 1 中的类 Employee 中添加例如以下字段 place 以及对应的 getter 和 setter 方法:
@PatternOfString.List({
@PatternOfString(mustContainLetter = "CH",
message = "It does not belong to China"),
@PatternOfString(mustContainLetter="MainLand",
message="It does not belong to MainLand")})
private String place;
然后在 main 函数中添加例如以下代码清单:
String place = "C";
employee.setPlace(place);
再次执行该程序。输出结果为:
It does not belong to MainLand
It does not belong to China
this string may be empty
The id of employee can not be null
The size of employee's name must between 1 and 10
假设将 place 赋值为 String place = "CHINA",则输出结果为:
this string may be empty
The id of employee can not be null
It does not belong to MainLand
The size of employee's name must between 1 and 10
可见,该约束会对声明的两个约束注解分别进行验证,仅仅要存在不符合约束验证规则的 Java Bean 实例,就将产生对应的验证失败信息。约束注解声明的时候能够依据不同的约束值使用 message 參数给出不同的输出信息。
组合约束
以下介绍 Bean Validation 规范中还有一个重要的特性:组合约束。
Bean Validation 规范同意将不同的约束进行组合来创建级别较高且功能较多的约束,从而避免原子级别约束的反复使用。如清单 4 定义的约束注解 @NotEmpty。是用来推断一个字符串在非空的基础上长度至少为 1。事实上际意义等同于 @NotNull 和 @Size(min=1)的组合形式。因此能够将 @NotEmpty 约束定义为组合约束 NotEmpty2,如清单 12 所看到的:
清单 12:
@NotNull
@Size(min = 1)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {NotEmptyValidator2.class})
public @interface NotEmpty2 {
String message() default "this string may be empty";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {}; @Target({ METHOD, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
@interface List {
NotEmpty2[] value();
}
}
实际使用中 @NotEmpty2 约束注解能够得到与 @NotEmpty 约束注解相同的验证结果。
约束的声明和验证流程
本文第二大部分介绍了怎样定义约束注解和验证器。本章主要介绍怎样在 Java Bean 中应用存在定义的约束注解,主要包含两部分:一是约束的声明。二是约束的验证流程。
在须要进行约束的目标元素前面用注解的方式就可以声明约束,这意味着该目标元素必须满足该约束的验证条件。如清单 13 即在字段 id 上声明了约束 @NotNull:
清单 13:
@NotNull(message = "The id of employee can not be null")
private Integer id;
该目标元素在详细实例中被赋值后,Bean Validation 就会调用相关的流程进行验证。详细使用方式能够參见清单 14 所看到的,当中所涉及的接口将在本文第四大部分详细介绍。
清单 14:
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<Employee>> set = validator.validate(JavaBeanInstance);
for (ConstraintViolation<Employee> constraintViolation : set) {
System.out.println(constraintViolation.getMessage());
}
Bean Validation 规范对 Java Bean 的验证流程例如以下:在实际使用中调用 Validator.validate(JavaBeanInstance) 方法后,Bean Validation 会查找在JavaBeanInstance上全部的约束声明,对每个约束调用相应的约束验证器进行验证,最后的结果由约束验证器的
isValid 方法产生,假设该方法返回 true,则约束验证成功,否则验证失败。
验证失败的约束将产生约束违规对象(ConstraintViolation 的实例)并放到约束违规列表中。验证完毕后全部的验证失败信息均能在该列表中查找并输出。
前提条件
Bean Validation 规范规定在对 Java Bean 进行约束验证前,目标元素必须满足下面条件:
- 假设验证的是属性(getter 方法)。那么必须遵从 Java Bean 的命名习惯(JavaBeans 规范);
- 静态的字段和方法不能进行约束验证;
- 约束适用于接口和基类。
- 约束注解定义的目标元素能够是字段、属性或者类型等。
- 能够在类或者接口上使用约束验证,它将对该类或实现该接口的实例进行状态验证;
- 字段和属性均能够使用约束验证,可是不能将同样的约束反复声明在字段和相关属性(字段的 getter 方法)上。
Object Graph 验证
除了支持 Java Bean 的实例验证外,Bean Validation 规范相同支持 Object Graph 的验证。Object Graph 即为对象的拓扑结构,如对象之间的引用关系。
假设类 A 引用类 B,则在对类 A 的实例进行约束验证时也须要对类 B 的实例进行约束验证,这就是验证的级联性。
当对 Java 语言中的集合、数组等类型进行验证时也须要对该类型的每个元素进行验证。
完毕级联验证的方式就是使用 @Valid 注解,如清单 15 所看到的:
清单 15:
public class Person {
@NotEmpty
private String name; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Order {
@Valid
private Person person; public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
在对 Order 的实例进行验证时,仅仅有当在 Order 引用的对象 Person 前面声明了注解 @Valid,才对 Person 中 name 字段的 @NotEmpty 注解进行验证,否则将不予验证。
组
Bean Validation 规范中一个重要的概念。就是组和组序列。组定义了约束的子集。对于一个给定的 Object Graph 结构,有了组的概念。则无需对该 Object Graph 中全部的约束进行验证。仅仅须要对该组定义的一个子集进行验证就可以。
完毕组别验证须要在约束声明时进行组别的声明。否则使用默认的组 Default.class.
组使用接口的方式进行定义,清单 16 给出了怎样定义组并使用组进行约束验证。
清单 16:
public interface GroupA {}
public class User {
@NotEmpty (message = "firstname may be empty")
private String firstname; @NotEmpty(message = "middlename may be empty", groups = Default.class)
private String middlename; @NotEmpty(message = "lastname may be empty",groups = GroupA.class)
private String lastname;
}
public static void main(String[] args){
User user = new User();
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<User>> set = validator.validate(user,GroupA.class);
for (ConstraintViolation<User> constraintViolation : set) {
System.out.println(constraintViolation.getMessage());
}
}
在类 User 中须要验证的字段上声明验证时所属的组别属性,如(groups=GroupA.class), 然后在 main 函数中调用 validator.validate(user,GroupA.class)) 方法。在此必须指定须要验证的组别。假设不显示指明。则是默认的组别。
如清单 16,验证器仅仅会验证类 User 的 lastname 字段,假设使用 validator.validate(user)),则会使用 Default.class 组别,从而验证 firstname 和 middlename 字段。
须要注意的是:组也有继承的属性。
对某一组别进行约束验证的时候,也会对其所继承的基类进行验证。
组能够进行隐式定义。其优点是能够不必在约束声明的时候显式声明组别属性,如清单 16 中的(groups=GroupA.class)。清单 17 给出了一个隐式定义的组接口(Animal),当中包括对对应属性(getter 方法)的约束声明。对应的 Java Bean(Dog)实现了该接口。
清单 17:
public interface Animal {
@NotEmpty String getName();
@NotEmpty String getOwnerName();
}
public class Dog implements Animal {
private String name;
private String ownername; private String type; public void setType(String type) {
this.type = type;
}
public String getName() {
return null;
}
public String getOwnerName() {
return null;
}
@NotEmpty(message = "type of the dog may be empty")
public String getType() {
return type;
}
}
这样在对类 Dog 的实例进行验证的时候,假设使用默认的组别(Default.class)。则 name,ownername 和 type 都将进行验证;假设使用 Animal 的组别,如清单 18 所看到的,则仅仅会对 name 和 ownername 属性进行验证。
清单 18:
public static void main(String[] args) {
Dog dog = new Dog();
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<Dog>> set = validator.validate(dog,Animal.class);
for (ConstraintViolation<Dog> constraintViolation : set) {
System.out.println(constraintViolation.getMessage());
}
}
输出结果为:
this string may be empty
this string may be empty
组序列
默认情况下。不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却非常重要,如以下两个样例:(1)第二个组中的约束验证依赖于一个稳定状态来执行。而这个稳定状态是由第一个组来进行验证的。
(2)某个组的验证比較耗时。CPU 和内存的使用率相对照较大。最优的选择是将其放在最后进行验证。
因此。在进行组验证的时候尚需提供一种有序的验证方式。这就提出了组序列的概念。
一个组能够定义为其它组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,假设序列前边的组验证失败,则后面的组将不再给予验证。
清单 19 声明了组 GroupA.class,GroupB.class 和 Group.class,当中 default,GroupA。GroupB 均为 Group 的序列。
清单 19:
public interface GroupA {
}
public interface GroupB {
}
@GroupSequence({Default.class, GroupA.class, GroupB.class})
public interface Group {
}
public class User {
@NotEmpty (message = "firstname may be empty")
private String firstname; @NotEmpty(message = "middlename may be empty", groups = Default.class)
private String middlename; @NotEmpty(message = "lastname may be empty",groups = GroupA.class)
private String lastname; @NotEmpty(message = "country may be empty",groups = GroupB.class)
private String country;
}
public static void main(String[] args){
User user = new User();
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<User>> set = validator.validate(user,Group.class);
for (ConstraintViolation<User> constraintViolation : set) {
System.out.println(constraintViolation.getMessage());
}
}
清单 19 中 main 函数的输出结果为:
middlename may be empty
firstname may be empty
从输出结果能够看出,该验证将不再为属于 GroupA 和 GroupB 的约束进行验证,由于属于组序列(Group.class)中前面位置的 Default 组验证失败。
仅仅有当在 main 函数增加例如以下代码片段使属于 Default 组别的验证通过后,方可进行兴许组别(GroupA。GroupB)的验证。
user.setFirstname("firstname");
user.setMiddlename("midlename");
穿透验证器(TrversableProperty)
穿透验证器主要适用于 JPA 规范,JPA 规范提供一种惰性连接属性,同意实体对象的某些字段被延迟载入,这些被延迟载入的字段须要 JPA 从底层数据库中获取。
Bean Validation 规范通过 TraversableResolver 接口来控制这类字段的存取性。在实际使用中须要先调用该接口中的 isReachable() 方法。假设返回 true,则证明该属性是可存取的。方可进行属性的约束验证。
相同,在进行级联验证时,也须要首先确定所引用的字段或者属性的可存取性方可进行约束的级联验证。
Bean Validation 规范接口及其可扩展的实现
本文前面的章节介绍了怎样定义约束注解以及怎样使用约束进行 Java Bean 验证。对于第三部分中提到的约束验证流程中的接口,本章将给予具体的介绍。
Bean Validation 规范同意用户定制个性化的约束验证,并给出了 4 大类接口供扩展使用。本章将结合 Bean Validation 规范的參考实现 Hibernate Validator4.0 进行说明。图 3 给出了 Bean
Validation 规范的 API 以及 Hibernate4.0 相关实现之间的关系示意图。
图 3. Bean Validation 接口以及 Hibernate4.0 接口实现示意图(查看大图)
- Bootstrapping 相关接口
Bootstrapping 相关接口提供 ValidatorFactory 对象,该对象负责创建 Validator(验证器)实例,该实例即是 Bean Validation client用来进行约束验证的主体类。Bootstrapping 相关接口主要包含 5 类,如表 2 所看到的:
表 2. Bootstrapping 相关接口及其作用
接口 作用 javax.validation.validation Bean Validation 规范的 API 默认提供该类,是整个 API 的入口。用来产生 Configuraton 对象实例。并启动环境中 ValidationProvider 的详细实现。 javax.validation.ValidationProviderResolver 返回运行上下文环境中全部的 BeanValidationProviders 的列表,并对每个 BeanValidationProvider 产生一个对象实例。 BeanValidation 规范提供一个默认的实现。
javax.validation.spi.ValidationProvider 详细的 BeanValidationProvider 实现须要实现该接口。该接口用来生成详细的 Congfiguration 接口的实现。 javax.validation.Configuration 收集上下文环境中的配置信息。主要用来计算怎样给定正确的 ValidationProvider,并将其委派给 ValidatorFactory 对象。 javax.validation.ValidatorFactory 从一个详细的 BeanValidationProvider 中构建 Validator 的实例。 - Validator 接口
该接口(javax.validation.Validator)定义了验证实例的方法,主要包含三种,如表 2 所看到的:
表 3. Validator 接口中的方法及其作用
方法名 作用 <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) 该方法用于验证一个给定的对象 <T>Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<? >...groups)
该方法用于验证给定对象中的字段或者属性 <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<? >... groups)
该方法用于验证给定对象中的属性的详细值 上述两类接口完毕验证器的初始化工作,以下使用清单 20 解释上述接口,在本文的演示样例中均使用 Hibernat Validator4.0 作为參考实现,因此上述两类接口的详细实现均是 Hibernat Validator4.0 包中的类。
清单 20:
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();清单 20 使用默认的方式创建验证工厂(ValidatorFactory),类 Validation 会检索类路径以下全部的 jar 文件,使用 ValidationProviderResolver 接口的默认实现 DefaultValidationProviderResolver(Bean Validation 规范提供该类)查找 META-INF/services/ 文件夹中的 javax.validation.spi.ValidationProvider 文件 , 在 Hibernate Validator4.0
中该文件里声明 org.hibernate.validator.HibernateValidator 类为 ValidationProvider 的详细实现,因此 Validation 调用 HibernateValidator 类创建 Configuration 接口的实例,在 Hibernate Validator4.0 中,该实例为 ConfigurationImpl。最后由 ConfigurationImpl 类产生 ValidatorFactory 的实例,在 HibernateValidator4.0
中为 ValidatorFactoryImpl 类。假设类路径中存在着多个该规范的实现,这就要用到 Configuration 接口去显示指定要使用的详细实现。然后再产生 ValidatorFactory 的实例。如清单 21 所看到的:
清单 21:
Configuration<HibernateValidatorConfiguration> config =
Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory vf = config.buildValidatorFactory();
Validator validator = vf.getValidator();假设想实现符合自身业务逻辑的 BeanValidationProvider 检索规则。仅仅须要实现接口 ValidationProviderResolver,而不是仅使用规范提供的默认实现。如清单 22 所看到的:
清单 22:
Configuration<? > config=Validation.byDefaultProvider().providerResolver(
new MyValidationProviderResolver()).configure();
ValidatorFactory vf = config.buildValidatorFactory();
Validator validator = vf.getValidator();清单 22 中 MyValidationProviderResolver 就是自己定义的检索规则,负责告诉 BeanValidation 怎样在详细环境中进行 BeanValidationProvider 的查找。
- ConstraintViolation 接口
该接口(javax.validation.ConstraintViolation)用来描写叙述某一验证的失败信息。
对某一个实体对象进行验证的时候,会返回 ConstraintViolation 的集合,如清单 23 所看到的:
清单 23:
Set<ConstraintViolation<Employee>> set = validator.validate(employee);
for (ConstraintViolation<Employee> constraintViolation : set) {
System.out.println(constraintViolation.getMessage());
} - MessageInterpolator 接口
该接口(javax.validation.MessageInterpolator)用来将验证过程中的失败消息以可读的方式传递给client使用者。Bean Validation 规范提供一个默认的消息解析接口,用户可自己定义符合自身业务需求的消息解析机制,仅仅需实现该接口就可以,如清单 24 所看到的。
清单 24:
Configuration<?> config = Validation.byDefaultProvider().configure();
config.messageInterpolator(new MyMessageInterpolator(config
.getDefaultMessageInterpolator()));当中 MyMessageInterpolator 就是自己定义的消息解析器,用来完毕特定的逻辑。
Bean Validation 规范的输出消息默认从类路径下的 ValidationMessage.properties 文件里读取。用户也能够在约束注解声明的时候使用 message 属性指定消息内容。
结束语
Bean Validation 规范使用注解的方式使 Java Bean 的验证机制更加灵活并且高效。本文对该规范进行了简单的介绍,旨在为 Java Bean 中业务逻辑验证机制的程序开发提供一个故意的參考。