Bean Validation 规范提供了一种查询约束存储库的方法。 该API有望用于工具支持以及与其他框架、库和JSR的集成。Bean Validation规范旨在为对象约束提供验证引擎和元数据存储库,需要约束定义,验证和元数据的框架(EE或SE)将能够依赖Bean Validation规范提供这些服务,从而避免了从应用程序和基础结构的角度进行任何不必要的重复工作
文章目录
5.1、 Validator
-
访问与给定对象相关的所有元数据的主要API是Validator(有关如何检索Valiator实例的更多信息,请参见第4.4节)
-
Validator实例托管用于访问给定类的元数据存储库的方法,建议将Validator实例的缓存留给ValidatorFactory. 验证程序的实现是线程安全的
-
/** * * 验证bean的实例,实现这个接口必须是线程安全的 * @author Emmanuel Bernard * @author Hardy Ferentschik */ public interface Validator{ [...]//查看4.1 /** * 返回描述bean约束的描述对象, 返回对象(和关联对象包含ConstraintDescriptor是不可变的 * @param clazz 类或接口类型 * * @return 返回特定类的bean描述 * @throws IllegalArgumentException 如果clazz为null * @throws 如果在元数据发现期间发生不可恢复的错误,或者某些约束无效,则抛出ValidationException * */ BeanDescriptor getConstraintsForClass(Class<?> clazz); }
-
getConstraintsForClass 返回一个 BeanDescriptor 描述bean等级约束的对象(详情查看3.1.1)和提供获取属性等级约束元数据。
-
如果一个约束定义或托管声明请求类(或任何子类或接口根据传播规则)是无效的,那么ValidationExcepton异常将会抛出,这个也可能是ValidationException 的子类,例如ConstraintDefinitionException, ConstraintDeclaraintDeclarationException, UnexpectedTypeException。
5.2、ElementDescriptor ( 元素描述接口)
-
ElementDescriptor是一个根接口用于描述元素托管包含约束。它用于描述给定元素(无论是字段,方法还是类)的约束列表
-
ElementDescriptor是在javax.validation.metadata包下
-
/** * * 描述一个验证的元素(类,字段或属性) * * @author Emmanuel Bernard * @author Hardy Ferentschik */ public interface ElementDescriptor{ /** * 如果对于这元素在类层级上至少一个约束声明存在的话, 则返回true * 反之返回false */ boolean hasConstraints(); /** * @return 静态定义的返回类型 * */ Class<?> getElementClass(); /** * 返回这个元素在类层级上所有约束描述集合,如果不存在返回空集合 * */ Set<ConstraintDescriptor<?>> getConstraintDescriptor(); /** * 查找约束并将其潜在第限制为某些条件。 * @return ConstraintFinder 对象 */ ConstraintFinder findConstraints(); /** * 声明对检索到的约束限制 * 限制是累积的 * ConstraintFinder不是线程安全。 这个集合匹配ConstraintDescriptor * */ interface ConstraintFinder{ /** * 限制与该元素的给定组集合匹配的约束 * 此方法遵循组序列和组继承(包含类级别的Default组覆盖,但是不会返回ConstraintDescriptor的任意特定顺序 * 具体而言,不遵循组序列的顺序。 * @param groups 组目标 * @return this 链式编程 * */ ConstraintFinder unorderedAndMatchingGroups(Class<?> ... groups); /** * 限制与该元素提供的范围相匹配的约束 * 默认是 Scope.HIERARCHY * @param scope 期望作用域 * @return this 链式编程 */ ConstraintFinder lookingAt(Scope scope); /** * 限制给定元素在列出的types上托管的约束 * 默认为元素的所有可能类型。 * 通常用于限制字段或getters方法 * @param types 目标类型 * @return this 链式编程 */ ConstraintFinder declaredOn(ElementType ... types); /** * 遵循定义的限制并在ElementDescirptor描述的元素上托管的约束描述符 * @return 匹配约束描述 * */ Set<ConstraintDescriptor<?>> getConstraintDescriptors(); /** * 如果元素上至少存在一个与限制匹配的限制声明,则返回true,否则则为false * @return 存在任何约束 * */ boolean hasConstraints(); } }
-
package javax.validation.metadata; /** * 查找约束的作用域 * * @author Emmanuel Bernard */ public enum Scope{ /** * 查找在当前类元素上声明的约束,并忽略继承和类层次结构中具有相同名称的元素。 * */ LOCAL_ELEMENT, /** * 查找在具有相同名称的类层次结构的所有元素上声明的约束。 * */ HIERARCHY }
-
getElementClass 返回的要么是这个对象类的类型,要么返回属性的类型
-
getConstraintDescriptors 返回给定元素的类层级上所有ConstraintDescriptor(详情查看5.5节),每个ConstraintDescriptor描述给定元素上的每个一个约束。
-
hasConstraints 如果给定元素(类、字段或属性)的类层级上有只有一个约束的声明将会返回true
-
如果你需要在更加精确的方式查询这个元数据API, 举个例子,通过约束的限制到一个描述在字段或在getter方法上或者给定约束到组的集合,你可以通过调用findConstraints使用ConstraintFider API
-
这里有个例子限制一系列约定到getters方法上,去匹配默认的组和声明在Customer属性name的getter方法上(在超类没有任何getters方法)
-
例子 5.1、 使用流利的API去限制匹配约束
-
public class User{ @Size(max=50) String getName(){...} ... } public class Customer extends User{ @NotNull String getName(){...} } PropertyDescriptor pd = validator.getConstraintsForClass(Customer.class).getConstraintsForProperty("name"); Set<ConstraintDescriptor<?>> constraints = pd.findConstraints() .declaredOn(ElementType.METHOD) .unorderedAndMatchingGroups(Default.class) .lookingAt(Scope.LOCAL_ELEMENT) .getConstraintDescriptors(); assert 1 == constraints.size(); constraints = pd.getConstraintDescriptors(); // 等同于 pd.findConstraints()..getConstraintDescriptors(); assert 2== constraints.size();
-
-
unorderedAndMatchingGroups 限制了 ConstraintDescriptors(查看5.5节)该约束与作为参数传递 并存在元素上的组的集合匹配。不遵循顺序,但是遵循组继承和通过序列的继承(包括在类级别覆盖的Default组)
-
declaredOn 让你限制元素类型的约束列表, 这个特别有用于在字段上声明了(ElementType.FIELD)约束或者是getters方法上(ElementType.METHOD)
-
lookingAt 使您可以限制考虑哪些约束。属于该元素但在BeanDescriptor表示的类(Scope.LOCAL_ELEMENT)上托管的约束,或者属于该元素但在类层次结构中的任意位置(Scope.HIERARCHY)托管的约束。
5.3、BeanDescriptor
-
BeanDescriptor描述是一个约束的java Bean, 通过调用Validator.getConstraintsForClass(Class<?>)返回BeanDescriptor对象
-
BeanDescriptor是在javax.validation.metadata包下
-
package javax.validation.metadata; /** * 描述一个约束的java bean并把约束关联它 * * * @author Emmanuel Bernard */ public interface BeanDescriptor extends ElementDescriptor{ /** * 如果这个bean进行约束验证那么返回true * 1. 约束在bean自身上 * 2. 约束在bean 一个属性上 * 3. 或者一个bean约束被标记为级联(@Valid) @return true or false */ boolean isBeanConstrained(); /** * 对于给定属性返回属性的描述符 * 如果属性不存在或没有约束标记为级联(查看 getConstrainedProperties()方法),则返回null, * 返回对象(和关联对象包含ConstraintDescriptor)都是不可变的 * @param propertyName 被评估的属性 * @return 对于给定属性返回属性的描述符 * @throws IllegalArgumentException 如果属性名称(propertyName)为null */ PropertyDescriptor getConstraintsForProperty(String propertyName); /** * 返回包含至少一个约束定义或标记为级联的属性描述集合, 如果没有匹配任何属性,将返回空集合。 * * */ Set<PropertyDescriptor> getConstrainedProperties(); }
-
isBeanConstrained 如果给定类(或超类和接口)至少有一个约束声明(或约束或@Valid注解)返回true, 如果这个方法返回false, 那么Bean Validation因将安全忽略这个bean,不会进行约束验证。
-
getConstraintsForProperty 返回一个PropertyDescriptor 对象去描述属性级别的约束(查看 3.1.2节),通过名称唯一标识属性作为javaBeans约定: 给属性名称对应字段级别和getter方法级别约束将会返回。
-
getConstrainedProperties 返回至少有一个约束或被级联(@Validation注解)的bean属性返回对应PropertyDescriptors集合。
5.4、PropertyDescriptor
-
PropertyDescriptor接口是描述Java Bean的属性的约束
-
PropertyDescriptor 在 javax.validation.metadata包下
-
这个接口可以通过BeanDescriptor.getConstraintsForProperty(String) 或 BeanDescriptor.getConstrainedProperties() 调用返回。 此描述符返回再属性上声明的约束和与Java Bean规则相同名称的getter方法。
-
package javax.validation.metadata; /** * 描述有约束验证的java bean的属性 * 约束声明在属性或getter方法上都会被引用到 * * @author Emmanuel Bernard */ public interface PropertyDescriptor extends ElementDescriptor{ /** * 判断属性是否被标记为(@Valid) * @return 如果@Valid注解存在那么返回true,否则返回false */ boolean isCascaded(); /** * 属性名称遵循 java bean 规范 * @return 属性名称 * */ String getPropertyName(); }
-
如果属性被标记为@Valid 那么这个调用isCascaded返回true
-
getPropertyName 返回属性名称(在4.2节描述过)
5.5、ConstraintDescriptor( 约束描述)
-
ConstraintDescriptor对象描述给定约束声明(也就是说,约束注解本身)
-
ConstraintDescriptor在 javax.validation.metadata包下
-
package javax.validation.metadata; /** * 描述单个约束和它组合约束, T是约束注解类型 * * @author Emmanuel Bernard * @author Hardy Ferentschik * */ public interface ConstraintDescriptor<T extends Annotation>{ /** * 返回描述这个约束的注解 * 如果是组合约束,则属性值反映了组成约束的覆盖属性 * @return 返回描述这个约束的注解 * */ T getAnnotation(); /** * 约束应用的所在组的集合 * 如果这个约束没有声明组信息,那么这个集合中只有一个Default组将被返回 * * @return 返回约束的组信息 * */ Set<Class<?>> getGroups(); /** * 返回约束的payload集合信息 * @return 返回这个这个类所有payload类,如果没有返回空集合 * */ Set<Class<? extend Payload>> getPayload(); /** * 列出这个约束验证实现类列表 * * @return 约束验证实现类列表 */ List<Class<? extends ConstraintValidator<T,?>>> get ConstraintValidatorClasses(); /** * 返回map,map的kay是属性名称,value是属性的值。如果这个约束适用组合约束, 那么属性 * 值映射到重写组合的约束 * @return 返回map属性键值对 */ Map<String, Object> getAttributes(); /** * 当每个描述符描述是一个组合约束时返回组合ConstraintDescriptor的集合, * @return 如果没有组合约束则返回空集合 */ Set<ConstraintDescriptor<?>> getComposingConstraints(); /** * *@return 如果约束标记为@ReportAsSingleViolation注解返回true * */ boolean isReportAsSingleViolation(); }
-
getAnnotation 返回注解的实例
-
getAttributes 返回map,map包含约束属性键值对
-
getGroups 返回组信息,如果没有声明组,默认返回包含Default元素的集合。
-
getPayload 返回 约束的payload集合
-
getConstraintValidatorClasses 返回ConstraintValidator关联这个约束的实现类
5.6、例子
-
假设@NotEmpty定义如下
-
@Documented @NotNull @Size(min=1) @ReportAsSingleViolation @Constraint(validateBy=NotEmpty.NotEmptyValidator.class) @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) public @interface NotEmpty{ String message() default "{com.acme.constraint.NotEmpty.message}"; Class<?> groups() default {}; @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented @interface List{ NotEmpty[] value(); } Class NotEmptyValidator implements ConstraintValidator<NotEmpty, String> { public void initialize(NotEmpty constraintAnnotation){} public boolean isValid(String value, ConstraintValidatorContext context){ return true; } } }
-
-
接下来类定义如下
-
public class Author{ private String firstName; @NotEmpty(message="lastname must not be null") private String lastName; @Size(max=30) private String company; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } } public class Book{ private String title; private String description; @Valid @NotNull private Author author; @NotEmpty(group={FirstLevelCheck.class, Default.class}) @Size(max=30) public String getTitle(){ return title; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } }
-
接下来是断言都是真
-
-
BeanDescriptor bookDescriptor = validator.getConstraintsForClass(Book.class); assert ! bookDescriptor.hasConstraints(); assert bookDescriptor.isBeanConstrained(); // 没有bean级别的约束 assert bookDescriptor.getConstraintDescriptors().size() == 0; //多个特定约束, "author" 和 "title" assert bookDescriptor.getConstrainedProperties.size() ==2; //不存在这个属性 assert bookDescriptor.getConstraintsForProperty("doesNotExist") == null; //这个属性不存在约束 assert bookDescriptor.getConstraintsForProperty("description") == null; ElementDescriptor propertyDescriptor = bookDescriptor.getConstraintsForProperty("title"); assert propertyDescriptor.getConstraintDescriptors().size() == 2; assert "title".equals(propertyDescriptor.getPropertyName()); //假设实现第一个返回@NotEmpty约束 ConstraintDescriptor<?> constraintDescriptor = propertyDescriptor.getConstraintDescriptors().iterator().next(); assert constraintDescriptor.getAnnotation().getAnnotationType().equals(NotEmpty.class); // 返回FirstLevelCheck 和 Default组 assert constraintDescriptor.getGroups().size() == 2; assert constraintDescriptor.getComposingConstraints().size()==2; assert constraintDescriptor.isReportAsSingleViolation() == true; // @NotEmpty 不能为null boolean notNullPresence = false; for(ConstraintDescriptor<?> composingDescriptor : constraintDescriptor.getComposingConstraints()){ if(composingDescriptor.getAnnotation().getAnnotationType().equals(NotNull.class)){ notNullPresence =true; } } assert notNullPresence; //假设返回第二个约束注解是@Size constraintDescriptor = propertyDescriptor.getConstraintDescriptors().iterator().next().next(); assert constraintDescriptor.getAnnotation().getAnnotationType().equals(Size.class); assert constraintDescriptor.getAttributes().get("max")==30; assert constraintDescriptor.getGroups().size() == 1; propertyDescriptor = bookDescriptor.getConstraintsForProperty("author"); assert propertyDescriptor.getConstraintDescriptors().size() ==1; assert propertyDescriptor.isCascaded();