13.07.2015 Views

JSR 303 的参考实现使用指南 - JBoss

JSR 303 的参考实现使用指南 - JBoss

JSR 303 的参考实现使用指南 - JBoss

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

序 言 ............................................................................. v1. 开 始 入 门 ..................................................................... 11.1. 第 一 个 Maven 项 目 ........................................................ 21.2. 添 加 约 束 .............................................................. 21.3. 校 验 约 束 .............................................................. 31.4. 更 进 一 步 .............................................................. 52. Validation step by step ...................................................... 72.1. 定 义 约 束 .............................................................. 72.1.1. 字 段 级 (field level) 约 束 ........................................ 72.1.2. 属 性 级 别 约 束 .................................................... 82.1.3. 类 级 别 约 束 ...................................................... 92.1.4. 约 束 继 承 ....................................................... 102.1.5. 对 象 图 ......................................................... 112.2. 校 验 约 束 .............................................................. 132.2.1. 获 取 一 个 Validator 的 实 例 ........................................ 132.2.2. Validator 中 的 方 法 .............................................. 132.2.3. ConstraintViolation 中 的 方 法 ................................... 142.2.4. 验 证 失 败 提 示 信 息 解 析 ........................................... 152.3. 校 验 组 ............................................................... 162.3.1. 校 验 组 序 列 ..................................................... 192.3.2. 对 一 个 类 重 定 义 其 默 认 校 验 组 ..................................... 202.4. 内 置 的 约 束 条 件 ........................................................ 222.4.1. Bean Validation constraints .................................... 222.4.2. Additional constraints ......................................... 253. 创 建 自 己 的 约 束 规 则 .......................................................... 293.1. 创 建 一 个 简 单 的 约 束 条 件 ................................................ 293.1.1. 约 束 标 注 ....................................................... 293.1.2. 约 束 校 验 器 ..................................................... 313.1.3. 校 验 错 误 信 息 ................................................... 343.1.4. 应 用 约 束 条 件 ................................................... 343.2. 约 束 条 件 组 合 .......................................................... 364. XML configuration ........................................................... 394.1. validation.xml ........................................................ 394.2. 映 射 约 束 .............................................................. 405. Bootstrapping ............................................................... 455.1. Configuration 和 ValidatorFactory ..................................... 455.2. ValidationProviderResolver ............................................ 465.3. MessageInterpolator ................................................... 475.3.1. ResourceBundleLocator .......................................... 475.4. TraversableResolver ................................................... 485.5. ConstraintValidatorFactory ............................................ 496. Metadata API ................................................................ 516.1. BeanDescriptor ........................................................ 516.2. PropertyDescriptor .................................................... 51iii


Hibernate Validator6.3. ElementDescriptor ..................................................... 516.4. ConstraintDescriptor .................................................. 527. 与 其 他 框 架 集 成 .............................................................. 537.1. OSGi ................................................................. 537.2. 与 数 据 库 集 成 校 验 ...................................................... 547.3. ORM 集 成 .............................................................. 547.3.1. 基 于 Hibernate 事 件 模 型 的 校 验 .................................... 547.3.2. JPA ............................................................ 557.4. 展 示 层 校 验 ............................................................ 568. Hibernate Validator Specifics ............................................... 578.1. Public API ........................................................... 578.2. Fail fast mode ....................................................... 598.3. Method validation .................................................... 608.3.1. Defining method-level constraints .............................. 608.3.2. Evaluating method-level constraints ............................ 628.3.3. Retrieving method-level constraint meta data ................... 648.4. Programmatic constraint definition .................................... 658.5. Boolean composition for constraint composition ........................ 679. Annotation Processor ........................................................ 699.1. 前 提 条 件 .............................................................. 699.2. 特 性 ................................................................. 699.3. 配 置 项 ............................................................... 709.4. 使 用 标 注 处 理 器 ........................................................ 709.4.1. 命 令 行 编 译 ..................................................... 709.4.2. IDE 集 成 ........................................................ 739.5. 已 知 问 题 .............................................................. 7510. 进 一 步 阅 读 ................................................................. 77iv


序 言数 据 校 验 是 任 何 一 个 应 用 程 序 都 会 用 到 的 功 能 , 无 论 是 显 示 层 还 是 持 久 层 . 通 常 , 相 同 的 校 验 逻 辑会 分 散 在 各 个 层 中 , 这 样 , 不 仅 浪 费 了 时 间 还 会 导 致 错 误 的 发 生 ( 译 注 : 重 复 代 码 ). 为 了 避 免 重复 , 开 发 人 员 经 常 会 把 这 些 校 验 逻 辑 直 接 写 在 领 域 模 型 里 面 , 但 是 这 样 又 把 领 域 模 型 代 码 和 校 验代 码 混 杂 在 了 一 起 , 而 这 些 校 验 逻 辑 更 应 该 是 描 述 领 域 模 型 的 元 数 据 .<strong>JSR</strong> <strong>303</strong> - Bean Validation - 为 实 体 验 证 定 义 了 元 数 据 模 型 和 API. 默 认 的 元 数 据 模 型 是 通 过Annotations 来 描 述 的 , 但 是 也 可 以 使 用 XML 来 重 载 或 者 扩 展 . Bean Validation API 并 不 局 限 于 应用 程 序 的 某 一 层 或 者 哪 种 编 程 模 型 , 例 如 , 如 图 所 示 , Bean Validation 可 以 被 用 在 任 何 一 层 , 或者 是 像 类 似 Swing 的 富 客 户 端 程 序 中 .Hibernate Validator is the reference implementation of this <strong>JSR</strong>. The implementationitself as well as the Bean Validation API and TCK are all provided and distributedunder the Apache Software License 2.0 [http://www.apache.org/licenses/LICENSE-2.0].v


第 1开 始 入 门本 章 将 会 告 诉 你 如 何 使 用 Hibernate Validator, 在 开 始 之 前 , 你 需 要 准 备 好 下 面 的 环 境 :•A JDK >= 5•Apache Maven [http://maven.apache.org/]• 网 络 连 接 ( Maven 需 要 通 过 互 联 网 下 载 所 需 的 类 库 )•A properly configured remote repository. Add the following to your settings.xml:例 1.1. Configuring the <strong>JBoss</strong> Maven repositoryjboss-public-repository-grouphttps://repository.jboss.org/nexus/content/groups/public-jbosstruetrueMore information about settings.xml can be found in the Maven Local Settings Model[http://maven.apache.org/ref/2.0.8/maven-settings/settings.html].注 意Hibernate Validator uses JAXB for XML parsing. JAXB is part of the JavaClass Library since Java 6 which means that if you run Hibernate Validatorwith Java 5 you will have to add additional JAXB dependencies. Using Mavenyou have to add the following dependencies:javax.xml.bindjaxb-api2.2com.sun.xml.bindjaxb-impl2.1.121


第 1 章 开 始 入 门if you are using the SourceForge package you find the necessarylibraries in the lib/jdk5 directory. In case you are not using theXML configuration you can also disable it explicitly by callingConfiguration.ignoreXmlConfiguration() during ValidationFactory creation. Inthis case the JAXB dependencies are not needed.1.1. 第 一 个 Maven 项 目使 用 Maven archetype 插 件 来 创 建 一 个 新 的 Maven 项 目例 1.2. 使 用 Maven archetype 插 件 来 创 建 一 个 简 单 的 基 于 Hibernate Validator的 项 目mvn archetype:generate -DarchetypeGroupId=org.hibernate \-DarchetypeArtifactId=hibernate-validator-quickstart-archetype \-DarchetypeVersion=4.2.0.Final \-DarchetypeRepository=http://repository.jboss.org/nexus/content/groups/public-jboss/ \-DgroupId=com.mycompany \-DartifactId=hv-quickstartMaven 将 会 把 你 的 项 目 创 建 在 hv-quickstart 目 录 中 . 进 入 这 个 目 录 并 且 执 行 :mvn test这 样 , Maven 会 编 译 示 例 代 码 并 且 运 行 单 元 测 试 , 接 下 来 , 让 我 们 看 看 生 成 的 代 码 .注 意From version 4.2.0.Beta2, the maven command mvn archetype:create will beno longer supported and will fail. You should use the command describedin the above listing. If you want more details, look at Maven Archetypeplugin [http://maven.apache.org/archetype/maven-archetype-plugin/] page.1.2. 添 加 约 束在 你 喜 欢 的 IDE 中 打 开 这 个 项 目 中 的 Car 类 :例 1.3. 带 约 束 性 标 注 (annotated with constraints) 的 Car 类package com.mycompany;2


校 验 约 束import javax.validation.constraints.Min;import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;public class Car {@NotNullprivate String manufacturer;@NotNull@Size(min = 2, max = 14)private String licensePlate;@Min(2)private int seatCount;public Car(String manufacturer, String licencePlate, int seatCount) {this.manufacturer = manufacturer;this.licensePlate = licencePlate;this.seatCount = seatCount;}}//getters and setters ...@NotNull, @Size and @Min 就 是 上 面 所 属 的 约 束 性 标 注 ( constraint annotations), 我 们 就 是 使 用它 们 来 声 明 约 束 , 例 如 在 Car 的 字 段 中 我 们 可 以 看 到 :•manufacturer 永 远 不 能 为 null•licensePlate 永 远 不 能 为 null, 并 且 它 的 值 字 符 串 的 长 度 要 在 2 到 14 之 间•seatCount 的 值 要 不 能 小 于 21.3. 校 验 约 束我 们 需 要 使 用 Validator 来 对 上 面 的 那 些 约 束 进 行 校 验 . 让 我 们 来 看 看 CarTest 这 个 类 :例 1.4. 在 CarTest 中 使 用 校 验package com.mycompany;import static org.junit.Assert.*;import java.util.Set;import javax.validation.ConstraintViolation;import javax.validation.Validation;import javax.validation.Validator;import javax.validation.ValidatorFactory;import org.junit.BeforeClass;import org.junit.Test;3


第 1 章 开 始 入 门public class CarTest {private static Validator validator;@BeforeClasspublic static void setUp() {ValidatorFactory factory = Validation.buildDefaultValidatorFactory();validator = factory.getValidator();}@Testpublic void manufacturerIsNull() {Car car = new Car(null, "DD-AB-123", 4);Set constraintViolations =validator.validate(car);}assertEquals(1, constraintViolations.size());assertEquals("may not be null", constraintViolations.iterator().next().getMessage());@Testpublic void licensePlateTooShort() {Car car = new Car("Morris", "D", 4);Set constraintViolations =validator.validate(car);assertEquals(1, constraintViolations.size());assertEquals("size must be between 2 and14", constraintViolations.iterator().next().getMessage());}@Testpublic void seatCountTooLow() {Car car = new Car("Morris", "DD-AB-123", 1);Set constraintViolations =validator.validate(car);assertEquals(1, constraintViolations.size());assertEquals("must be greater than or equal to2", constraintViolations.iterator().next().getMessage());}@Testpublic void carIsValid() {Car car = new Car("Morris", "DD-AB-123", 2);Set constraintViolations =validator.validate(car);}}assertEquals(0, constraintViolations.size());4


更 进 一 步在 setUp() 方 法 中 , 我 们 通 过 ValidatorFactory 得 到 了 一 个 Validator 的 实 例 . Validator 是 线 程 安 全 的 ,并 且 可 以 重 复 使 用 , 所 以 我 们 把 它 保 存 成 一 个 类 变 量 . 现 在 我 们 可 以 在 test 方 法 中 使 用 这 个validator 的 实 例 来 校 验 不 同 的 car 实 例 了 .validate() 方 法 会 返 回 一 个 set 的 ConstraintViolation 的 实 例 的 集 合 , 我 们 可 以 通 过 遍 历 它 来 查 看 有哪 些 验 证 错 误 . 前 面 三 个 测 试 用 例 显 示 了 一 些 预 期 的 校 验 约 束 :• 在 manufacturerIsNull() 中 可 以 看 到 manufacturer 违 反 了 @NotNull 约 束•licensePlateTooShort() 中 的 licensePlate 违 反 了 @Size 约 束• 而 seatCountTooLow() 中 则 导 致 seatCount 违 反 了 @Min 约 束如 果 一 个 对 象 没 有 校 验 出 问 题 的 话 , 那 么 validate() 会 返 回 一 个 空 的 set 对 象 .注 意 , 我 们 只 使 用 了 Bean Validation API 中 的 package javax.validation 中 的 类 , 并 没 有 直 接 调用 参 考 实 现 中 的 任 何 类 , 所 以 , 没 有 任 何 问 题 如 果 切 换 到 其 他 的 实 现 .1.4. 更 进 一 步That concludes our 5 minute tour through the world of Hibernate Validator. Continueexploring the code examples or look at further examples referenced in 第 10 章 进 一 步 阅读 . To deepen your understanding of Hibernate Validator just continue reading 第 2 章Validation step by step. In case your application has specific validation requirementshave a look at 第 3 章 创 建 自 己 的 约 束 规 则 .5


第 2Validation step by step在 本 章 中 , 我 们 会 详 细 的 介 绍 如 何 使 用 Hibernate Validator 来 对 一 个 给 定 的 实 体 模 型 进 行 验 证 .还 会 介 绍 Bean Validation 规 范 提 供 了 哪 些 默 认 的 约 束 条 件 和 Hibernate Validator 提 供 了 哪 些 额外 的 . 让 我 们 先 从 如 何 给 一 个 实 体 添 加 约 束 开 始 .2.1. 定 义 约 束Bean Validation 的 约 束 是 通 过 Java 注 解 (annotations) 来 标 注 的 . 在 本 节 中 , 我 们 会 介 绍 如 何 使用 这 些 注 解 (annotations) 来 标 注 一 个 实 体 模 型 . 并 且 , 我 们 会 区 分 三 种 不 通 的 注 解 (annotations)类 型 .注 意不 是 所 有 的 约 束 都 能 够 被 用 在 所 有 的 类 结 构 上 . 事 实 上 , 没 有 任 何 定义 在 Bean Validation 规 范 中 的 约 束 可 以 被 用 在 class 上 . 约 束 定 义 中的 java.lang.annotation.Target 属 性 定 义 了 这 个 约 束 能 够 被 使 用 在 哪 个 层 次 结 构 上 .详 细 信 息 请 参 考 第 3 章 创 建 自 己 的 约 束 规 则 .2.1.1. 字 段 级 (field level) 约 束约 束 条 件 能 够 被 标 注 在 类 的 字 段 上 面 , 请 参 考 示 例 例 2.1 “ 字 段 级 (field level) 约 束 ”例 2.1. 字 段 级 (field level) 约 束package com.mycompany;import javax.validation.constraints.NotNull;public class Car {@NotNullprivate String manufacturer;@AssertTrueprivate boolean isRegistered;}public Car(String manufacturer, boolean isRegistered) {super();this.manufacturer = manufacturer;this.isRegistered = isRegistered;}7


第 2 章 Validation step by step当 约 束 被 定 义 在 字 段 上 的 时 候 , 这 个 字 段 的 值 是 通 过 字 段 访 问 策 略 来 获 取 并 验 证 的 . 也 就 是 说Bean Validation 的 实 现 者 会 直 接 访 问 这 个 实 例 变 量 而 不 会 调 用 属 性 的 访 问 器 (getter) 即 使 这 个方 法 存 在 .注 意这 个 字 段 的 访 问 级 别 ( private, protected 或 者 public) 对 此 没 有 影 响 .注 意静 态 字 段 或 者 属 性 是 不 会 被 校 验 的 .2.1.2. 属 性 级 别 约 束如 果 你 的 模 型 遵 循 JavaBeans [http://java.sun.com/javase/technologies/desktop/javabeans/index.jsp] 规 范 的 话 , 你 还 可 以 把 约 束 标 注 在 属 性 上 . 例 2.2 “ 属 性 级 约 束 ” 和 例 2.1 “ 字 段级 (field level) 约 束 ” 的 唯 一 不 同 就 是 它 的 约 束 是 定 义 在 属 性 级 别 上 的 .注 意如 果 要 定 义 约 束 在 属 性 级 别 上 的 话 , 那 么 只 能 定 义 在 访 问 器 (getter) 上 面 , 不 能 定义 在 修 改 器 (setter) 上 .例 2.2. 属 性 级 约 束package com.mycompany;import javax.validation.constraints.AssertTrue;import javax.validation.constraints.NotNull;public class Car {private String manufacturer;private boolean isRegistered;public Car(String manufacturer, boolean isRegistered) {super();this.manufacturer = manufacturer;this.isRegistered = isRegistered;}@NotNullpublic String getManufacturer() {return manufacturer;}8


类 级 别 约 束public void setManufacturer(String manufacturer) {}this.manufacturer = manufacturer;@AssertTruepublic boolean isRegistered() {return isRegistered;}}public void setRegistered(boolean isRegistered) {}this.isRegistered = isRegistered;When using property level constraints property access strategy is used to access thevalue to be validated. This means the bean validation provider accesses the state viathe property accessor method. One advantage of annotating properties instead of fieldsis that the constraints become part of the constrained type's API that way and users areaware of the existing constraints without having to examine the type's implementation.提 示It is recommended to stick either to field or property annotations withinone class. It is not recommended to annotate a field and the accompanyinggetter method as this would cause the field to be validated twice.2.1.3. 类 级 别 约 束最 后 , 一 个 约 束 也 能 够 被 放 在 类 级 别 上 . 当 一 个 约 束 被 标 注 在 一 个 类 上 的 时 候 , 这 个 类 的 实 例 对 象被 传 递 给 ConstraintValidator. 当 需 要 同 时 校 验 多 个 属 性 来 验 证 一 个 对 象 或 者 一 个 属 性 在 验 证 的 时候 需 要 另 外 的 属 性 的 信 息 的 时 候 , 类 级 别 的 约 束 会 很 有 用 . 在 例 2.3 “ 类 级 别 约 束 ” 中 , 我 们给 类 Car 添 加 了 一 个 passengers 的 属 性 . 并 且 我 们 还 标 注 了 一 个 PassengerCount 约 束 在 类 级 别 上 . 稍后 会 看 到 我 们 是 如 何 创 建 这 个 自 定 义 的 约 束 的 ( 第 3 章 创 建 自 己 的 约 束 规 则 ). 现 在 , 我 们 可 以 知道 ,PassengerCount 会 保 证 这 个 车 里 乘 客 的 数 量 不 会 超 过 它 的 座 位 数 .例 2.3. 类 级 别 约 束package com.mycompany;import javax.validation.constraints.Min;import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;@PassengerCountpublic class Car {@NotNullprivate String manufacturer;9


第 2 章 Validation step by step@NotNull@Size(min = 2, max = 14)private String licensePlate;@Min(2)private int seatCount;private List passengers;public Car(String manufacturer, String licencePlate, int seatCount) {this.manufacturer = manufacturer;this.licensePlate = licencePlate;this.seatCount = seatCount;}}//getters and setters ...2.1.4. 约 束 继 承如 果 要 验 证 的 对 象 继 承 于 某 个 父 类 或 者 实 现 了 某 个 接 口 , 那 么 定 义 在 父 类 或 者 接 口 中 的 约 束 会 在 验证 这 个 对 象 的 时 候 被 自 动 加 载 , 如 同 这 些 约 束 定 义 在 这 个 对 象 所 在 的 类 中 一 样 . 让 我 们 来 看 看 下 面的 示 例 :例 2.4. 约 束 继 承package com.mycompany;import javax.validation.constraints.NotNull;public class RentalCar extends Car {private String rentalStation;public RentalCar(String manufacturer, String rentalStation) {super(manufacturer);this.rentalStation = rentalStation;}@NotNullpublic String getRentalStation() {return rentalStation;}}public void setRentalStation(String rentalStation) {}this.rentalStation = rentalStation;我 们 有 了 一 个 新 的 RentalCar 类 继 承 自 前 面 我 们 已 经 见 到 的 Car, 这 个 子 类 中 增 加 了 一 个rentalStation 属 性 . 如 果 校 验 一 个 RentalCar 的 实 例 对 象 , 那 么 不 仅 会 验 证 属 性 rentalStation 上的 @NotNull 约 束 是 否 合 法 , 还 会 校 验 父 类 中 的 manufacturer 属 性 .10


对 象 图如 果 类 Car 是 一 个 接 口 类 型 的 话 也 是 一 样 的 效 果 .如 果 类 RentalCar 重 写 了 父 类 Car 的 getManufacturer() 方 法 , 那 么 定 义 在 父 类 的 这 个 方 法 上 的 约 束 和子 类 这 个 方 法 上 定 义 的 约 束 都 会 被 校 验 .2.1.5. 对 象 图Bean Validation API 不 仅 能 够 用 来 校 验 单 个 的 实 例 对 象 , 还 能 够 用 来 校 验 完 整 的 对 象 图 . 要 使 用 这个 功 能 , 只 需 要 在 一 个 有 关 联 关 系 的 字 段 或 者 属 性 上 标 注 @Valid. 这 样 , 如 果 一 个 对 象 被 校 验 , 那 么它 的 所 有 的 标 注 了 @Valid 的 关 联 对 象 都 会 被 校 验 . 请 看 例 2.6 “Adding a driver to the car”.例 2.5. Class Personpackage com.mycompany;import javax.validation.constraints.NotNull;public class Person {@NotNullprivate String name;public Person(String name) {super();this.name = name;}public String getName() {}return name;}public void setName(String name) {}this.name = name;例 2.6. Adding a driver to the carpackage com.mycompany;import javax.validation.Valid;import javax.validation.constraints.NotNull;public class Car {@NotNull@Validprivate Person driver;public Car(Person driver) {}this.driver = driver;11


第 2 章 Validation step by step}//getters and setters ...如 果 校 验 Car 的 实 例 对 象 的 话 , 因 为 它 的 driver 属 性 标 注 了 @Valid, 那 么 关 联 的 Person 也 会 被 校 验 .所 以 , 如 果 对 象 Person 的 name 属 性 如 果 是 null 的 话 , 那 么 校 验 会 失 败 .关 联 校 验 也 适 用 于 集 合 类 型 的 字 段 , 也 就 是 说 , 任 何 下 列 的 类 型 :• 数 组• 实 现 了 java.lang.Iterable 接 口 ( 例 如 Collection, List 和 Set)• 实 现 了 java.util.Map 接 口如 果 标 注 了 @Valid, 那 么 当 主 对 象 被 校 验 的 时 候 , 这 些 集 合 对 象 中 的 元 素 都 会 被 校 验 .例 2.7. Car with a list of passengerspackage com.mycompany;import java.util.ArrayList;import java.util.List;import javax.validation.Valid;import javax.validation.constraints.NotNull;public class Car {@NotNull@Validprivate List passengers = new ArrayList();public Car(List passengers) {}this.passengers = passengers;}//getters and setters ...当 校 验 一 个 Car 的 实 例 的 时 候 , 如 果 passengers list 中 包 含 的 任 何 一 个 Person 对 象 没 有 名 字 的 话 , 都会 导 致 校 验 失 败 (a ConstraintValidation will be created).注 意对 象 图 校 验 的 时 候 是 会 被 忽 略 null 值 的 .12


校 验 约 束2.2. 校 验 约 束Validator 是 Bean Validation 中 最 主 要 的 接 口 , 我 们 会 在 第 5.1 节 “Configuration和 ValidatorFactory” 中 详 细 介 绍 如 何 获 取 一 个 Validator 的 实 例 , 现 在 先 让 我 们 来 看 看 如 何 使用 Validator 接 口 中 的 各 个 方 法 .2.2.1. 获 取 一 个 Validator 的 实 例对 一 个 实 体 对 象 验 证 之 前 首 先 需 要 有 个 Validator 对 象 , 而 这 个 对 象 是 需 要 通 过 Validation 类 和ValidatorFactory 来 创 建 的 . 最 简 单 的 方 法 是 调 用 Validation.buildDefaultValidatorFactory() 这 个静 态 方 法 .例 2.8. Validation.buildDefaultValidatorFactory()ValidatorFactory factory = Validation.buildDefaultValidatorFactory();Validator validator = factory.getValidator();第 5 章 Bootstrapping 介 绍 了 其 他 的 获 取 Validator 实 例 的 方 法 . 现 在 我 们 的 目 标 是 学 习 如 何 使用 Validator 来 校 验 实 体 对 象 .2.2.2. Validator 中 的 方 法Validator 中 有 三 个 方 法 能 够 被 用 来 校 验 整 个 实 体 对 象 或 者 实 体 对 象 中 的 属 性 .这 三 个 方 法 都 会 返 回 一 个 Set 对 象 , 如 果 整 个 验 证 过 程 没 有 发 现 问 题 的 话 , 那么 这 个 set 是 空 的 , 否 则 , 每 个 违 反 约 束 的 地 方 都 会 被 包 装 成 一 个 ConstraintViolation 的 实 例 然 后添 加 到 set 当 中 .所 有 的 校 验 方 法 都 接 收 零 个 或 多 个 用 来 定 义 此 次 校 验 是 基 于 哪 个 校 验 组 的 参 数 . 如 果 没 有 给 出 这个 参 数 的 话 , 那 么 此 次 校 验 将 会 基 于 默 认 的 校 验 组 (javax.validation.groups.Default). 第 2.3 节“ 校 验 组 ”2.2.2.1. validate使 用 validate() 方 法 对 一 个 给 定 的 实 体 对 象 中 定 义 的 所 有 约 束 条 件 进 行 校 验 ( 例 2.9“Validator.validate() 使 用 方 法 ” ).例 2.9. Validator.validate() 使 用 方 法ValidatorFactory factory = Validation.buildDefaultValidatorFactory();Validator validator = factory.getValidator();Car car = new Car(null);Set constraintViolations = validator.validate(car);13


第 2 章 Validation step by stepassertEquals(1, constraintViolations.size());assertEquals("may not be null", constraintViolations.iterator().next().getMessage());2.2.2.2. validateProperty通 过 validateProperty() 可 以 对 一 个 给 定 实 体 对 象 的 单 个 属 性 进 行 校 验 . 其 中 属 性 名 称 需 要 符 合JavaBean 规 范 中 定 义 的 属 性 名 称 .例 2.10. Validator.validateProperty() 使 用 方 法Validator validator = Validation.buildDefaultValidatorFactory().getValidator();Car car = new Car(null);Set constraintViolations = validator.validateProperty(car, "manufacturer");assertEquals(1, constraintViolations.size());assertEquals("may not be null", constraintViolations.iterator().next().getMessage());例 如 , Validator.validateProperty 可 以 被 用 在 把 Bean Validation 集 成 进 JSF 2 中 的 时 候 使 用 ( 请 参考 第 7.4 节 “ 展 示 层 校 验 ”).2.2.2.3. validateValue通 过 validateValue() 方 法 , 你 能 够 校 验 如 果 把 一 个 特 定 的 值 赋 给 一 个 类 的 某 一 个 属 性 的 话 , 是 否 会违 反 此 类 中 定 义 的 约 束 条 件 .例 2.11. Validator.validateValue() 使 用 方 法Validator validator = Validation.buildDefaultValidatorFactory().getValidator();Set constraintViolations = validator.validateValue(Car.class, "manufacturer", null);assertEquals(1, constraintViolations.size());assertEquals("may not be null", constraintViolations.iterator().next().getMessage());注 意validateProperty() 和 validateValue() 会 忽 略 被 验 证 属 性 上 定 义 的 @Valid.2.2.3. ConstraintViolation 中 的 方 法现 在 是 时 候 看 看 究 竟 ConstraintViolation 是 什 么 了 . ConstraintViolation 中 包 含 了 很 多 方 法 能 够 帮你 快 速 定 位 究 竟 是 什 么 导 致 了 校 验 失 败 . 表 2.1 “ConstraintViolation 中 的 方 法 ” 列 出 了 这 些方 法 :14


验 证 失 败 提 示 信 息 解 析表 2.1. ConstraintViolation 中 的 方 法方 法 名 作 用 示 例 ( 请 参 考 例 2.9“Validator.validate() 使 用方 法 ”)getMessage()获 取 ( 经 过 翻 译 的 ) 校 验 错 误 信息may not be nullgetMessageTemplate() 获 取 错 误 信 息 模 版 {javax.validation.constraints.NotNull.messagegetRootBean() 获 取 被 校 验 的 根 实 体 对 象 cargetRootBeanClass() 获 取 被 校 验 的 根 实 体 类 . Car.classgetLeafBean()getPropertyPath()如 果 约 束 是 添 加 在 一 个bean( 实 体 对 象 ) 上 的 , 那 么 则返 回 这 个 bean 的 实 例 , 如 果 是约 束 是 定 义 在 一 个 属 性 上 的 ,则 返 回 这 个 属 性 所 属 的 bean 的实 例 对 象 .从 被 验 证 的 根 对 象 到 被 验 证 的属 性 的 路 径 .cargetInvalidValue() 倒 是 校 验 失 败 的 值 . passengersgetConstraintDescriptor() 导 致 校 验 失 败 的 约 束 定 义 .2.2.4. 验 证 失 败 提 示 信 息 解 析在 第 3 章 创 建 自 己 的 约 束 规 则 中 , 我 们 会 看 到 , 每 个 约 束 定 义 中 都 包 含 有 一 个 用 于 提 示 验 证 结果 的 消 息 模 版 , 并 且 在 声 明 一 个 约 束 条 件 的 时 候 , 你 可 以 通 过 这 个 约 束 中 的 message 属 性 来 重 写 默 认的 消 息 模 版 , 可 以 参 考 例 2.13 “Driver”. 如 果 在 校 验 的 时 候 , 这 个 约 束 条 件 没 有 通 过 , 那么 你 配 置 的 MessageInterpolator 会 被 用 来 当 成 解 析 器 来 解 析 这 个 约 束 中 定 义 的 消 息 模 版 , 从 而 得 到最 终 的 验 证 失 败 提 示 信 息 . 这 个 解 析 器 会 尝 试 解 析 模 版 中 的 占 位 符 ( 大 括 号 括 起 来 的 字 符 串 ).其 中 , Hibernate Validator 中 默 认 的 解 析 器 (MessageInterpolator) 会 先 在 类 路 径 下 找 名 称为 ValidationMessages.properties 的 ResourceBundle, 然 后 将 占 位 符 和 这 个 文 件 中 定 义 的 resource 进行 匹 配 , 如 果 匹 配 不 成 功 的 话 , 那 么 它 会 继 续 匹 配 Hibernate Validator 自 带 的 位 于 /org/hibernate/validator/ValidationMessages.properties 的 ResourceBundle, 依 次 类 推 , 递 归 的 匹 配 所 有 的 占 位 符 .因 为 大 括 号 { 在 这 里 是 特 殊 字 符 , 所 以 , 你 可 以 通 过 使 用 反 斜 线 来 对 其 进 行 转 义 , 例 如 :•\{ 被 转 义 成 {•\} 被 转 义 成 }•\\ 被 转 义 成 \如 果 默 认 的 消 息 解 析 器 不 能 够 满 足 你 的 需 求 , 那 么 你 也 可 以 在 创 建 ValidatorFactory 的 时 候 , 将 其替 换 为 一 个 你 自 定 义 的 MessageInterpolator, 具 体 请 参 考 第 5 章 Bootstrapping.15


第 2 章 Validation step by step2.3. 校 验 组校 验 组 能 够 让 你 在 验 证 的 时 候 选 择 应 用 哪 些 约 束 条 件 . 这 样 在 某 些 情 况 下 ( 例 如 向 导 ) 就 可 以对 每 一 步 进 行 校 验 的 时 候 , 选 取 对 应 这 步 的 那 些 约 束 条 件 进 行 验 证 了 . 校 验 组 是 通 过 可 变 参 数 传递 给 validate, validateProperty 和 validateValue 的 . 让 我 们 来 看 个 例 子 , 这 个 实 例 扩 展 了 上 面的 Car 类 , 又 为 其 添 加 了 一 个 新 的 Driver. 首 先 , 类 Person ( 例 2.12 “Person”) 的 name 属 性 上 定义 了 一 个 @NotNull 的 约 束 条 件 . 因 为 没 有 明 确 指 定 这 个 约 束 条 件 属 于 哪 个 组 , 所 以 它 被 归 类 到 默 认组 (javax.validation.groups.Default).注 意如 果 某 个 约 束 条 件 属 于 多 个 组 , 那 么 各 个 组 在 校 验 时 候 的 顺 序 是 不 可 预 知 的 . 如 果一 个 约 束 条 件 没 有 被 指 明 属 于 哪 个 组 , 那 么 它 就 会 被 归 类 到 默 认 组(javax.validation.groups.Default).例 2.12. Personpublic class Person {@NotNullprivate String name;}public Person(String name) {this.name = name;}// getters and setters ...接 下 来 , 我 们 让 类 Driver ( 例 2.13 “Driver”) 继 承 自 类 Person. 然 后 添 加 两 个 属 性 , 分 别 是age 和 hasDrivingLicense. 对 于 一 个 司 机 来 说 , 要 开 车 的 话 , 必 须 满 足 两 个 条 件 , 年 满 18 周 岁(@Min(18)) 和 你 的 有 驾 照 (@AssertTrue). 这 两 个 约 束 条 件 分 别 定 义 在 那 两 个 属 性 上 , 并 且 把 他 们都 归 于 DriverChecks 组 . 通 过 例 2.14 “Group interfaces”, 你 可 以 看 到 , "DriverChecks 组 " 就是 一 个 简 单 的 标 记 接 口 . 使 用 接 口 ( 而 不 是 字 符 串 ) 可 以 做 到 类 型 安 全 , 并 且 接 口 比 字 符 串 更 加 对重 构 友 好 , 另 外 , 接 口 还 意 味 着 一 个 组 可 以 继 承 别 的 组 .例 2.13. Driverpublic class Driver extends Person {@Min(value = 18, message = "You have to be 18 to drive a car", groups = DriverChecks.class)public int age;@AssertTrue(message = "You first have to pass the driving test", groups = DriverChecks.class)public boolean hasDrivingLicense;public Driver(String name) {}super( name );16


校 验 组public void passedDrivingTest(boolean b) {}hasDrivingLicense = b;public int getAge() {}return age;}public void setAge(int age) {}this.age = age;例 2.14. Group interfacespublic interface DriverChecks {}public interface CarChecks {}最 后 , 我 们 给 Car class ( 例 2.15 “Car”) 添 加 一 个 passedVehicleInspection 的 属 性 , 来 表 示这 个 车 是 否 通 过 了 上 路 检 查 .例 2.15. Carpublic class Car {@NotNullprivate String manufacturer;@NotNull@Size(min = 2, max = 14)private String licensePlate;@Min(2)private int seatCount;@AssertTrue(message = "The car has to pass the vehicle inspectionfirst", groups = CarChecks.class)private boolean passedVehicleInspection;@Validprivate Driver driver;}public Car(String manufacturer, String licencePlate, int seatCount) {this.manufacturer = manufacturer;this.licensePlate = licencePlate;this.seatCount = seatCount;}17


第 2 章 Validation step by step现 在 , 在 我 们 的 例 子 中 有 三 个 不 同 的 校 验 组 , Person.name, Car.manufacturer,Car.licensePlate 和 Car.seatCount 都 属 于 默 认 (Default) 组 , Driver.age 和Driver.hasDrivingLicense 从 属 于 DriverChecks 组 , 而 Car.passedVehicleInspection在 CarChecks 组 中 . 例 2.16 “Drive away” 演 示 了 如 何 让 Validator.validate 验 证 不 同 的 组 来 得 到不 同 的 校 验 结 果 .例 2.16. Drive awaypublic class GroupTest {private static Validator validator;@BeforeClasspublic static void setUp() {ValidatorFactory factory = Validation.buildDefaultValidatorFactory();validator = factory.getValidator();}@Testpublic void driveAway() {// create a car and check that everything is ok with it.Car car = new Car( "Morris", "DD-AB-123", 2 );Set constraintViolations = validator.validate( car );assertEquals( 0, constraintViolations.size() );// but has it passed the vehicle inspection?constraintViolations = validator.validate( car, CarChecks.class );assertEquals( 1, constraintViolations.size() );assertEquals("The car has to pass the vehicle inspectionfirst", constraintViolations.iterator().next().getMessage());// let's go to the vehicle inspectioncar.setPassedVehicleInspection( true );assertEquals( 0, validator.validate( car ).size() );// now let's add a driver. He is 18, but has not passed the driving test yetDriver john = new Driver( "John Doe" );john.setAge( 18 );car.setDriver( john );constraintViolations = validator.validate( car, DriverChecks.class );assertEquals( 1, constraintViolations.size() );assertEquals( "You first have to pass the drivingtest", constraintViolations.iterator().next().getMessage() );// ok, John passes the testjohn.passedDrivingTest( true );assertEquals( 0, validator.validate( car, DriverChecks.class ).size() );}}// just checking that everything is in order nowassertEquals( 0, validator.validate( car, Default.class, CarChecks.class, DriverChecks.class ).size() );18


校 验 组 序 列首 先 我 们 创 建 一 辆 汽 车 然 后 在 没 有 明 确 指 定 使 用 哪 个 校 验 组 的 情 况 下 校 验 它 , 可 以 看 到 即 使passedVehicleInspection 的 默 认 值 是 false 也 不 会 校 验 出 错 误 来 . 因 为 定 义 在 这 个 属 性 上 的 约 束条 件 并 不 属 于 默 认 的 校 验 组 , 接 下 来 , 我 们 来 校 验 CarChecks 这 个 组 , 这 样 就 会 发 现 car 违 反 了 约 束条 件 , 必 须 让 这 个 车 先 通 过 检 测 . 接 下 来 , 我 们 给 这 个 车 增 加 一 个 司 机 , 然 后 在 基 于 DriverChecks 来校 验 , 会 发 现 因 为 这 个 司 机 因 为 还 没 有 通 过 驾 照 考 试 , 所 以 又 一 次 得 到 了 校 验 错 误 , 如 果 我 们 设置 passedDrivingTest 属 性 为 true 之 后 , DriverChecks 组 的 校 验 就 通 过 了 .最 后 , 让 我 们 再 来 校 验 所 有 的 组 中 定 义 的 约 束 条 件 , 可 以 看 到 所 有 的 约 束 条 件 都 通 过 了 验 证 .2.3.1. 校 验 组 序 列By default, constraints are evaluated in no particular order and this regardless ofwhich groups they belong to. In some situations, however, it is useful to controlthe order of the constraint evaluation. In our example from 第 2.3 节 “ 校 验 组 ” wecould for example require that first all default car constraints are passing beforewe check the road worthiness of the car. Finally before we drive away we check theactual driver constraints. In order to implement such an order one would define a newinterface and annotate it with @GroupSequence defining the order in which the groupshave to be validated.注 意如 果 这 个 校 验 组 序 列 中 有 一 个 约 束 条 件 没 有 通 过 验 证 的 话 , 那 么 此 约 束 条 件 后 面 的都 不 会 再 继 续 被 校 验 了 .例 2.17. 标 注 了 @GroupSequence 的 接 口@GroupSequence({Default.class, CarChecks.class, DriverChecks.class})public interface OrderedChecks {}警 告一 个 校 验 组 序 列 中 包 含 的 校 验 组 和 这 个 校 验 组 序 列 不 能 造 成 直 接 或 者 间 接 的 循 环引 用 . 包 括 校 验 组 继 承 . 如 果 造 成 了 循 环 引 用 的 话 , 会 导致 GroupDefinitionException 异 常 .例 2.18 “ 校 验 组 序 列 的 用 法 ” 展 示 了 校 验 组 序 列 的 用 法 .例 2.18. 校 验 组 序 列 的 用 法@Test19


第 2 章 Validation step by steppublic void testOrderedChecks() {Car car = new Car( "Morris", "DD-AB-123", 2 );car.setPassedVehicleInspection( true );Driver john = new Driver( "John Doe" );john.setAge( 18 );john.passedDrivingTest( true );car.setDriver( john );}assertEquals( 0, validator.validate( car, OrderedChecks.class ).size() );2.3.2. 对 一 个 类 重 定 义 其 默 认 校 验 组2.3.2.1. @GroupSequenceThe @GroupSequence annotation also fulfills a second purpose. It allows you to redefinewhat the Default group means for a given class. To redefine Default for a given class, adda @GroupSequence annotation to the class. The defined groups in the annotation expressthe sequence of groups that substitute Default for this class. 例 2.19 “RentalCar with@GroupSequence” introduces a new class RentalCar with a redefined default group. Withthis definition the check for all three groups can be rewritten as seen in 例 2.20“testOrderedChecksWithRedefinedDefault”.例 2.19. RentalCar with @GroupSequence@GroupSequence({ RentalCar.class, CarChecks.class, DriverChecks.class })public class RentalCar extends Car {private boolean rented;public RentalCar(String manufacturer, String licencePlate, int seatCount) {}super( manufacturer, licencePlate, seatCount );public boolean isRented() {}return rented;}public void setRented(booelan rented) {}this.rented = rented;例 2.20. testOrderedChecksWithRedefinedDefault@Testpublic void testOrderedChecksWithRedefinedDefault() {RentalCar rentalCar = new RentalCar( "Morris", "DD-AB-123", 2 );rentalCar.setPassedVehicleInspection( true );20


对 一 个 类 重 定 义 其 默 认 校 验 组Driver john = new Driver( "John Doe" );john.setAge( 18 );john.passedDrivingTest( true );rentalCar.setDriver( john );}assertEquals( 0, validator.validate( rentalCar, Default.class ).size() );注 意因 为 不 能 在 校 验 组 和 校 验 组 序 列 中 有 循 环 依 赖 关 系 , 所 以 , 如 果 你 想 重 定 义 一 个 类的 默 认 组 , 并 且 还 想 把 Default 组 加 入 到 这 个 重 定 义 的 序 列 当 中 的 话 , 则 不 能 简 单 的加 入 Default, 而 是 需 要 把 被 重 定 义 的 类 加 入 到 其 中 .2.3.2.2. @GroupSequenceProviderThe @javax.validation.GroupSequence annotation is a standardized Bean Validationannotation. As seen in the previous section it allows you to statically redefinethe default group sequence for a class. Hibernate Validator also offers a custom,non standardized annotation - org.hibernate.validator.group.GroupSequenceProvider - whichallows for dynamic redefinition of the default group sequence. Using the rental carscenario again, one could dynamically add the driver checks depending on whetherthe car is rented or not. 例 2.21 “RentalCar with @GroupSequenceProvider” and 例“DefaultGroupSequenceProvider implementation” show how this use-case would beimplemented.例 2.21. RentalCar with @GroupSequenceProvider@GroupSequenceProvider(RentalCarGroupSequenceProvider.class)public class RentalCar extends Car {private boolean rented;public RentalCar(String manufacturer, String licencePlate, int seatCount) {}super( manufacturer, licencePlate, seatCount );public boolean isRented() {}return rented;}public void setRented(boolean rented) {}this.rented = rented;21


Bean Validation constraintsAnnotationSupported data作 用Hibernate metadatatypesimpact@AssertTrue Boolean, boolean Checks that theannotated elementis true.没 有@DecimalMaxBigDecimal,被 标 注 的 值 必 须没 有BigInteger, String,不 大 于 约 束 中 指 定byte, short, int,的 最 大 值 . 这 个 约long and the束 的 参 数 是 一 个 通respective wrappers过 BigDecimal 定 义 的 最of the primitive大 值 的 字 符 串 表 示 .types. Additionallysupported by HV:any sub-type ofNumber.@DecimalMinBigDecimal,被 标 注 的 值 必 须没 有BigInteger, String,不 小 于 约 束 中 指 定byte, short, int,的 最 小 值 . 这 个 约long and the束 的 参 数 是 一 个 通respective wrappers过 BigDecimal 定 义 的 最of the primitive小 值 的 字 符 串 表 示 .types. Additionallysupported by HV:any sub-type ofNumber.@Digits(integer=,BigDecimal,Checks whether the对 应 的 数 据 库 表fraction=)BigInteger, String,annoted value is a字 段 会 被 设 置 精 度byte, short, int,number having up to(precision) 和 准 度long and theinteger digits and(scale).respective wrappersfraction fractionalof the primitivedigits.types. Additionallysupported by HV:any sub-type ofNumber.@Futurejava.util.Date, 检 查 给 定 的 日 期 是 否java.util.Calendar; 比 现 在 晚 .Additionallysupported by HV,if the Joda Time[http://jodatime.sourceforge.net/] date/time API is没 有23


第 2 章 Validation step by stepAnnotationSupported data作 用Hibernate metadatatypesimpacton the class path:any implementationsof ReadablePartialand ReadableInstant.@MaxBigDecimal,检 查 该 值 是 否 小 于 或会 给 对 应 的 数 据 库 表BigInteger, byte,等 于 约 束 条 件 中 指 定字 段 添 加 一 个 check 的short, int, long的 最 大 值 .约 束 条 件 .and the respectivewrappers of theprimitive types.Additionallysupported by HV:String (the numericvalue representedby a String isevaluated), anysub-type of Number.@MinBigDecimal,检 查 该 值 是 否 大 于 或会 给 对 应 的 数 据 库 表BigInteger, byte,等 于 约 束 条 件 中 规 定字 段 添 加 一 个 check 的short, int, long的 最 小 值 .约 束 条 件 .and the respectivewrappers of theprimitive types.Additionallysupported by HV:String (the numericvalue representedby a String isevaluated), anysub-type of Number.@NotNull Any type Checks that theannotated value isnot null.@Null Any type Checks that theannotated value isnull.对 应 的 表 字 段 不 允 许为 null.没 有@Pastjava.util.Date,检 查 标 注 对 象 中 的 值没 有java.util.Calendar;表 示 的 日 期 比 当 前 早 .Additionallysupported by HV,if the Joda Time24


Additional constraintsAnnotationSupported data 作 用types[http://jodatime.sourceforge.net/] date/time API ison the class path:any implementationsof ReadablePartialand ReadableInstant.Hibernate metadataimpact@Pattern(regex=,String检 查 该 字 符 串 是 否 能没 有flag=)够 在 match 指 定 的 情 况下 被 regex 定 义 的 正 则表 达 式 匹 配 .@Size(min=, max=)String, Collection,Checks if the对 应 的 数 据 库 表 字 段Map and arraysannotated element's的 长 度 会 被 设 置 成 约size is between束 中 定 义 的 最 大 值 .min and max(inclusive).@ValidAny non-primitive递 归 的 对 关 联 对 象 进没 有type行 校 验 , 如 果 关 联 对象 是 个 集 合 或 者 数 组 ,那 么 对 其 中 的 元 素 进行 递 归 校 验 , 如 果 是 一个 map, 则 对 其 中 的 值部 分 进 行 校 验 .注 意On top of the parameters indicated in 表 2.2 “Bean Validationconstraints” each constraint supports the parameters message, groups andpayload. This is a requirement of the Bean Validation specification.2.4.2. Additional constraintsIn addition to the constraints defined by the Bean Validation API Hibernate Validatorprovides several useful custom constraints which are listed in 表 2.3 “Customconstraints provided by Hibernate Validator”. With one exception also these constraintsapply to the field/property level, only @ScriptAssert is a class-level constraint.25


第 2 章 Validation step by step表 2.3. Custom constraints provided by Hibernate ValidatorAnnotationSupported data作 用Hibernate metadatatypesimpact@CreditCardNumber String Checks thatthe annotatedstring passesthe Luhn checksumtest. Note, thisvalidation aimsto check foruser mistakes,not credit cardvalidity! Seealso Anatomyof Credit CardNumbers [http://www.merriampark.com/anatomycc.htm].@Email String Checks whether thespecified stringis a valid emailaddress.没 有没 有@Length(min=, max=) StringValidates that theannotated string isbetween min and maxincluded.对 应 的 数 据 库 表 字 段的 长 度 会 被 设 置 成 约束 中 定 义 的 最 大 值 .@NotBlank String Checks that theannotated stringis not null andthe trimmed lengthis greater than 0.The difference to@NotEmpty is thatthis constraintcan only be appliedon strings andthat trailingwhitespaces areignored.没 有@NotEmptyString, Collection,Checks whether the没 有Map and arraysannotated element26


Additional constraintsAnnotationSupported data作 用Hibernate metadatatypesimpactis not null norempty.@Range(min=, max=)BigDecimal,Checks whether没 有BigInteger, String,the annotatedbyte, short, int,value lies betweenlong and the(inclusive) therespective wrappersspecified minimumof the primitiveand maximum.types@SafeHtml(whitelistType=, CharSequenceadditionalTags=)Checks whetherthe annotatedvalue containspotentiallymalicious fragmentssuch as . In order to usethis constraint,the jsoup [http://jsoup.org/] librarymust be part of theclass path. Withthe whitelistTypeattributepredefinedwhitelist typescan be chosen. Youcan also specifyadditional htmltags for thewhitelist withthe additionalTagsattribute.没 有@ScriptAssert(lang=,script=, alias=)Any type 要 使 用 这 个 约 束 条 件 ,必 须 先 要 保 证 JavaScripting API 即<strong>JSR</strong> 223 ("Scriptingfor the Java TMPlatform") 的 实 现 在类 路 径 当 中 . 如 果 使用 的 时 Java 6 的 话 ,则 不 是 问 题 , 如 果没 有27


第 2 章 Validation step by stepAnnotationSupported data作 用Hibernate metadatatypesimpact是 老 版 本 的 话 , 那 么需 要 把 <strong>JSR</strong> 223 的 实现 添 加 进 类 路 径 . 这个 约 束 条 件 中 的 表 达式 可 以 使 用 任 何 兼 容<strong>JSR</strong> 223 的 脚 本 来 编写 . ( 更 多 信 息 请 参 考javadoc)@URL(protocol=,StringChecks if the没 有host=, port=,annotated stringregexp=, flags=)is a valid URLaccording toRFC2396. If anyof the optionalparameters protocol,host or port arespecified, thecorresponding URLfragments mustmatch the specifiedvalues. Theoptional parametersregexp and flagsallow to specifyan additionalregular expression(including regularexpression flags)which the URL mustmatch.In some cases neither the Bean Validation constraints nor the custom constraintsprovided by Hibernate Validator will fulfill your requirements completely. In thiscase you can literally in a minute write your own constraints. We will discuss thisin 第 3 章 创 建 自 己 的 约 束 规 则 .28


第 3创 建 自 己 的 约 束 规 则尽 管 Bean Validation API 定 义 了 一 大 堆 标 准 的 约 束 条 件 , 但 是 肯 定 还 是 有 这 些 约 束 不 能 满 足 我 们需 求 的 时 候 , 在 这 种 情 况 下 , 你 可 以 根 据 你 的 特 定 的 校 验 需 求 来 创 建 自 己 的 约 束 条 件 .3.1. 创 建 一 个 简 单 的 约 束 条 件按 照 以 下 三 个 步 骤 来 创 建 一 个 自 定 义 的 约 束 条 件• 创 建 约 束 标 注• 实 现 一 个 验 证 器• 定 义 默 认 的 验 证 错 误 信 息3.1.1. 约 束 标 注让 我 们 来 创 建 一 个 新 的 用 来 判 断 一 个 给 定 字 符 串 是 否 全 是 大 写 或 者 小 写 字 符 的 约 束 标 注 . 我 们 将稍 后 把 它 用 在 第 1 章 开 始 入 门 中 的 类 Car 的 licensePlate 字 段 上 来 确 保 这 个 字 段 的 内 容 一 直 都 是大 写 字 母 .首 先 , 我 们 需 要 一 种 方 法 来 表 示 这 两 种 模 式 ( 译 注 : 大 写 或 小 写 ), 我 们 可 以 使 用 String 常 量 , 但 是在 Java 5 中 , 枚 举 类 型 是 个 更 好 的 选 择 :例 3.1. 枚 举 类 型 CaseMode, 来 表 示 大 写 或 小 写 模 式 .package com.mycompany;public enum CaseMode {UPPER,LOWER;}现 在 我 们 可 以 来 定 义 真 正 的 约 束 标 注 了 . 如 果 你 以 前 没 有 创 建 过 标 注 (annotation) 的 话 , 那 么 这 个可 能 看 起 来 有 点 吓 人 , 可 是 其 实 没 有 那 么 难 的 :)例 3.2. 定 义 一 个 CheckCase 的 约 束 标 注package com.mycompany;import static java.lang.annotation.ElementType.*;import static java.lang.annotation.RetentionPolicy.*;import java.lang.annotation.Documented;import java.lang.annotation.Retention;import java.lang.annotation.Target;29


第 3 章 创 建 自 己 的 约 束 规 则import javax.validation.Constraint;import javax.validation.Payload;@Target( { METHOD, FIELD, ANNOTATION_TYPE })@Retention(RUNTIME)@Constraint(validatedBy = CheckCaseValidator.class)@Documentedpublic @interface CheckCase {String message() default "{com.mycompany.constraints.checkcase}";Class[] groups() default {};Class 类 型 到 空 到 数 组 .•payload 属 性 , Bean Validation API 的 使 用 者 可 以 通 过 此 属 性 来 给 约 束 条 件 指 定 严 重 级 别 . 这个 属 性 并 不 被 API 自 身 所 使 用 .提 示通 过 payload 属 性 来 指 定 默 认 错 误 严 重 级 别 的 示 例public class Severity {public static class Info extends Payload {};public static class Error extends Payload {};}public class ContactDetails {@NotNull(message="Name is mandatory", payload=Severity.Error.class)private String name;@NotNull(message="Phone number not specified, but not mandatory",payload=Severity.Info.class)private String phoneNumber;}// ...30


约 束 校 验 器这 样 , 在 校 验 完 一 个 ContactDetails 的 示 例 之 后 , 你 就 可 以 通 过 调用 ConstraintViolation.getConstraintDescriptor().getPayload() 来 得 到 之 前 指 定到 错 误 级 别 了 , 并 且 可 以 根 据 这 个 信 息 来 决 定 接 下 来 到 行 为 .除 了 这 三 个 强 制 性 要 求 的 属 性 (message, groups 和 payload) 之 外 , 我 们 还 添加 了 一 个 属 性 用 来 指 定 所 要 求 到 字 符 串 模 式 . 此 属 性 的 名 称 value 在 annotation 的 定 义 中 比 较 特殊 , 如 果 只 有 这 个 属 性 被 赋 值 了 的 话 , 那 么 , 在 使 用 此 annotation 到 时 候 可 以 忽 略 此 属 性 名 称 ,即 @CheckCase(CaseMode.UPPER).另 外 , 我 们 还 给 这 个 annotation 标 注 了 一 些 ( 所 谓 的 ) 元 标 注 ( 译 注 : 或 " 元 模 型 信 息 "?, "metaannotatioins"):•@Target({ METHOD, FIELD, ANNOTATION_TYPE }): 表 示 @CheckCase 可 以 被 用 在 方 法 , 字 段 或 者annotation 声 明 上 .•@Retention(RUNTIME): 表 示 这 个 标 注 信 息 是 在 运 行 期 通 过 反 射 被 读 取 的 .•@Constraint(validatedBy = CheckCaseValidator.class): 指 明 使 用 那 个 校 验 器 ( 类 ) 去 校 验 使 用 了此 标 注 的 元 素 .•@Documented: 表 示 在 对 使 用 了 @CheckCase 的 类 进 行 javadoc 操 作 到 时 候 , 这 个 标 注 会 被 添 加 到javadoc 当 中 .提 示Hibernate Validator provides support for the validation of methodparameters using constraint annotations (see 第 8.3 节 “Methodvalidation”).In order to use a custom constraint for parameter validation theElementType.PARAMETER must be specified within the @Target annotation. Thisis already the case for all constraints defined by the Bean ValidationAPI and also the custom constraints provided by Hibernate Validator.3.1.2. 约 束 校 验 器Next, we need to implement a constraint validator, that's able to validate elementswith a @CheckCase annotation. To do so, we implement the interface ConstraintValidatoras shown below:例 3.3. 约 束 条 件 CheckCase 的 验 证 器package com.mycompany;31


第 3 章 创 建 自 己 的 约 束 规 则import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;public class CheckCaseValidator implements ConstraintValidator {private CaseMode caseMode;public void initialize(CheckCase constraintAnnotation) {}this.caseMode = constraintAnnotation.value();public boolean isValid(String object, ConstraintValidatorContext constraintContext) {if (object == null)return true;}if (caseMode == CaseMode.UPPER)return object.equals(object.toUpperCase());elsereturn object.equals(object.toLowerCase());}ConstraintValidator 定 义 了 两 个 泛 型 参 数 , 第 一 个 是 这 个 校 验 器 所 服 务 到 标 注 类 型 ( 在 我 们 的 例 子中 即 CheckCase), 第 二 个 这 个 校 验 器 所 支 持 到 被 校 验 元 素 到 类 型 ( 即 String).如 果 一 个 约 束 标 注 支 持 多 种 类 型 到 被 校 验 元 素 的 话 ,个 ConstraintValidator, 并 且 注 册 到 约 束 标 注 中 .那 么 需 要 为 每 个 所 支 持 的 类 型 定 义 一这 个 验 证 器 的 实 现 就 很 平 常 了 , initialize() 方 法 传 进 来 一 个 所 要 验 证 的 标 注 类 型 的 实 例 , 在 本例 中 , 我 们 通 过 此 实 例 来 获 取 其 value 属 性 的 值 , 并 将 其 保 存 为 CaseMode 类 型 的 成 员 变 量 供 下 一 步 使用 .isValid() 是 实 现 真 正 的 校 验 逻 辑 的 地 方 , 判 断 一 个 给 定 的 String 对 于 @CheckCase 这 个 约 束 条 件 来 说是 否 是 合 法 的 , 同 时 这 还 要 取 决 于 在 initialize() 中 获 得 的 大 小 写 模 式 . 根 据 Bean Validation 中 所推 荐 的 做 法 , 我 们 认 为 null 是 合 法 的 值 . 如 果 null 对 于 这 个 元 素 来 说 是 不 合 法 的 话 , 那 么 它 应 该 使用 @NotNull 来 标 注 .3.1.2.1. ConstraintValidatorContext例 3.3 “ 约 束 条 件 CheckCase 的 验 证 器 ” 中 的 isValid 使 用 了 约 束 条 件 中 定 义 的 错 误 消 息 模 板 , 然后 返 回 一 个 true 或 者 false. 通 过 使 用 传 入 的 ConstraintValidatorContext 对 象 , 我 们 还 可 以 给 约 束条 件 中 定 义 的 错 误 信 息 模 板 来 添 加 额 外 的 信 息 或 者 完 全 创 建 一 个 新 的 错 误 信 息 模 板 .例 3.4. 使 用 ConstraintValidatorContext 来 自 定 义 错 误 信 息package com.mycompany;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;32


约 束 校 验 器public class CheckCaseValidator implements ConstraintValidator {private CaseMode caseMode;public void initialize(CheckCase constraintAnnotation) {}this.caseMode = constraintAnnotation.value();public boolean isValid(String object, ConstraintValidatorContext constraintContext) {if (object == null)return true;boolean isValid;if (caseMode == CaseMode.UPPER) {isValid = object.equals(object.toUpperCase());}else {isValid = object.equals(object.toLowerCase());}}if(!isValid) {constraintContext.disableDefaultConstraintViolation();constraintContext.buildConstraintViolationWithTemplate( "{com.mycompany.constraints.CheckCase.message}}return result;}例 3.4 “ 使 用 ConstraintValidatorContext 来 自 定 义 错 误 信 息 ” 演 示 了 如 果 创建 一 个 新 的 错 误 信 息 模 板 来 替 换 掉 约 束 条 件 中 定 义 的 默 认 的 . 在 本 例 中 , 实 际 上 通 过 调用 ConstraintValidatorContext 达 到 了 一 个 使 用 默 认 消 息 模 板 的 效 果 .提 示在 创 建 新 的 constraint violation 的 时 候 一 定 要 记 得 调 用 addConstraintViolation,只 有 这 样 , 这 个 新 的 constraint violation 才 会 被 真 正 的 创 建 .In case you are implementing a ConstraintValidator a class level constraint it is alsopossible to adjust set the property path for the created constraint violations. Thisis important for the case where you validate multiple properties of the class or eventraverse the object graph. A custom property path creation could look like 例 3.5“Adding new ConstraintViolation with custom property path”.例 3.5. Adding new ConstraintViolation with custom property pathpublic boolean isValid(Group group, ConstraintValidatorContext constraintValidatorContext) {boolean isValid = false;...33


第 3 章 创 建 自 己 的 约 束 规 则}if(!isValid) {constraintValidatorContext.buildConstraintViolationWithTemplate( "{my.custom.template}" ).addNode( "myProperty" ).addConstraintViolation();}return isValid;3.1.3. 校 验 错 误 信 息最 后 , 我 们 还 需 要 指 定 如 果 @CheckCase 这 个 约 束 条 件 验 证 的 时 候 , 没 有 通 过 的 话 的 校 验 错 误 信 息 .我 们 可 以 添 加 下 面 的 内 容 到 我 们 项 目 自 定 义 的 ValidationMessages.properties ( 参 考 第 2.2.4 节“ 验 证 失 败 提 示 信 息 解 析 ”) 文 件 中 .例 3.6. 为 CheckCase 约 束 定 义 一 个 错 误 信 息com.mycompany.constraints.CheckCase.message=Case mode must be {value}.如 果 发 现 校 验 错 误 了 的 话 , 你 所 使 用 的 Bean Validation 的 实 现 会 用 我 们 定 义 在 @CheckCase 中message 属 性 上 的 值 作 为 键 到 这 个 文 件 中 去 查 找 对 应 的 错 误 信 息 .3.1.4. 应 用 约 束 条 件现 在 我 们 已 经 有 了 一 个 自 定 义 的 约 束 条 件 了 , 我 们 可 以 把 它 用 在 第 1 章 开 始 入 门 中 的 Car 类 上 ,来 校 验 此 类 的 licensePlate 属 性 的 值 是 否 全 都 是 大 写 字 母 .例 3.7. 应 用 CheckCase 约 束 条 件package com.mycompany;import javax.validation.constraints.Min;import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;public class Car {@NotNullprivate String manufacturer;@NotNull@Size(min = 2, max = 14)@CheckCase(CaseMode.UPPER)private String licensePlate;@Min(2)private int seatCount;public Car(String manufacturer, String licencePlate, int seatCount) {this.manufacturer = manufacturer;34


应 用 约 束 条 件}this.licensePlate = licencePlate;this.seatCount = seatCount;//getters and setters ...}最 后 , 让 我 们 用 一 个 简 单 的 测 试 来 检 测 @CheckCase 约 束 已 经 被 正 确 的 校 验 了 :例 3.8. 演 示 CheckCase 的 验 证 过 程package com.mycompany;import static org.junit.Assert.*;import java.util.Set;import javax.validation.ConstraintViolation;import javax.validation.Validation;import javax.validation.Validator;import javax.validation.ValidatorFactory;import org.junit.BeforeClass;import org.junit.Test;public class CarTest {private static Validator validator;@BeforeClasspublic static void setUp() {ValidatorFactory factory = Validation.buildDefaultValidatorFactory();validator = factory.getValidator();}@Testpublic void testLicensePlateNotUpperCase() {Car car = new Car("Morris", "dd-ab-123", 4);}Set constraintViolations =validator.validate(car);assertEquals(1, constraintViolations.size());assertEquals("Case mode must be UPPER.",constraintViolations.iterator().next().getMessage());@Testpublic void carIsValid() {Car car = new Car("Morris", "DD-AB-123", 4);Set constraintViolations =validator.validate(car);35


第 3 章 创 建 自 己 的 约 束 规 则}}assertEquals(0, constraintViolations.size());3.2. 约 束 条 件 组 合在 例 3.7 “ 应 用 CheckCase 约 束 条 件 ” 中 我 们 可 以 看 到 , 类 Car 的 licensePlate 属 性 上 定 义 了 三 个约 束 条 件 . 在 某 些 复 杂 的 场 景 中 , 可 能 还 会 有 更 多 的 约 束 条 件 被 定 义 到 同 一 个 元 素 上 面 , 这 可 能会 让 代 码 看 起 来 有 些 复 杂 , 另 外 , 如 果 在 另 外 的 类 里 面 还 有 一 个 licensePlate 属 性 , 我 们 可 能 还要 把 这 些 约 束 条 件 再 拷 贝 到 这 个 属 性 上 , 但 是 这 样 做 又 违 反 了 DRY 原 则 .这 个 问 题 可 以 通 过 使 用 组 合 约 束 条 件 来 解 决 . 接 下 来 让 我 们 来 创 建 一 个 新 的 约 束 标注 @ValidLicensePlate, 它 组 合 了 @NotNull, @Size 和 @CheckCase:例 3.9. 创 建 一 个 约 束 条 件 组 合 ValidLicensePlatepackage com.mycompany;import static java.lang.annotation.ElementType.*;import static java.lang.annotation.RetentionPolicy.*;import java.lang.annotation.Documented;import java.lang.annotation.Retention;import java.lang.annotation.Target;import javax.validation.Constraint;import javax.validation.Payload;import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;@NotNull@Size(min = 2, max = 14)@CheckCase(CaseMode.UPPER)@Target( { METHOD, FIELD, ANNOTATION_TYPE })@Retention(RUNTIME)@Constraint(validatedBy = {})@Documentedpublic @interface ValidLicensePlate {String message() default "{com.mycompany.constraints.validlicenseplate}";Class[] groups() default {};Class


约 束 条 件 组 合现 在 , 在 licensePlate 属 性 上 使 用 这 个 新 定 义 的 " 约 束 条 件 " ( 其 实 是 个 组 合 ) 和 之 前 在 其 上 声 明那 三 个 约 束 条 件 是 一 样 的 效 果 了 .例 3.10. 使 用 ValidLicensePlate 组 合 约 束package com.mycompany;public class Car {@ValidLicensePlateprivate String licensePlate;//...}The set of ConstraintViolations retrieved when validating a Car instance will contain anentry for each violated composing constraint of the @ValidLicensePlate constraint. Ifyou rather prefer a single ConstraintViolation in case any of the composing constraintsis violated, the @ReportAsSingleViolation meta constraint can be used as follows:例 3.11. @ReportAsSingleViolation 的 用 法//...@ReportAsSingleViolationpublic @interface ValidLicensePlate {String message() default "{com.mycompany.constraints.validlicenseplate}";Class[] groups() default {};Class


第 4XML configuration4.1. validation.xml我 们 可 以 使 用 validation.xml 来 对 Hibernate Validator 进 行 配 置 . ValidationFactory 在 初 始 化 的 时候 会 在 类 路 径 下 寻 找 此 文 件 , 如 果 找 到 的 话 , 就 会 应 用 其 中 定 义 的 配 置 信 息 . 例 4.1 “validationconfiguration-1.0.xsd”显 示 了 valiation.xml 的 xsd 模 型 .例 4.1. validation-configuration-1.0.xsd例 4.2 “validation.xml” 列 出 了 validation.xml 中 的 一 些 常 用 的 配 置 项 .例 4.2. validation.xmlorg.hibernate.validator.HibernateValidatororg.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolatororg.hibernate.validator.engine.resolver.DefaultTraversableResolverorg.hibernate.validator.engine.ConstraintValidatorFactoryImpl/constraints-car.xml39


第 4 章 XML configurationvalue1value2警 告类 路 径 下 面 只 能 有 一 个 validation.xml, 如 果 超 过 一 个 的 话 , 会 抛 出 异 常 .validation.xml 中 所 有 的 配 置 信 息 都 是 可 选 的 , 例 4.2 “validation.xml” 中 就 是 列 出 了Hibernate Validator 中 的 默 认 值 . 如 果 类 路 径 当 中 存 在 有 多 个 Bean Validation 的 实 现 的话 , 那 么 可 以 通 过 default-provider 节 点 指 定 使 用 那 个 Bean Validation 的 实 现 . messageinterpolator,traversable-resolver 和 constraint-validator-factory 可 以 用 来 指 定 自 定 义的 javax.validation.MessageInterpolator,javax.validation.TraversableResolver 和 javax.validation.ConstraintValidatorFactory. 同 样 的 , 这些 配 置 信 息 也 可 以 通 过 编 程 的 方 式 调 用 javax.validation.Configuration 来 实 现 . 另 外 , 你 可 以 通 过API 的 方 式 来 重 写 xml 中 的 配 置 信 息 , 也 可 以 通 过 调 用 Configuration.ignoreXmlConfiguration() 来 完全 的 忽 略 掉 xml 的 配 置 信 息 . 请 参 考 第 5 章 Bootstrapping.你 可 以 增 加 若 干 个 constraint-mapping 节 点 , 在 每 个 里 面 列 出 一 个 额 外 的 xml 文 件 用 来 定 义 约 束 规则 , 具 体 请 参 考 第 4.2 节 “ 映 射 约 束 ”.Last but not least, you can specify provider specific properties via the property nodes.4.2. 映 射 约 束我 们 也 可 以 通 过 xml 来 定 义 约 束 条 件 , 只 需 要 这 个 xml 符 合 例 4.3 “validationmapping-1.0.xsd”中 所 定 义 的 规 范 . 需 要 注 意 的 是 , 你 必 须 把 xml 定 义 的 约 束 列在 validation.xml 的 constraint-mapping 节 点 中 才 能 得 到 处 理 .40


映 射 约 束例 4.3. validation-mapping-1.0.xsd41


第 4 章 XML configuration例 4.4 “constraints-car.xml” 显 示 了 如 何 通 过 xml 定 义 的 方 式 来 给 例 2.15 “Car” 中 的 类 Car以 及 例 2.19 “RentalCar with @GroupSequence” 中 的 RentalCar 定 义 约 束 条 件 .例 4.4. constraints-car.xmlorg.hibernate.validator.quickstart2The car has to pass the vehicle inspection firstCarChecks10RentalCarCarChecksorg.mycompany.CheckCaseValidator这 个 xml 的 定 义 基 本 上 和 通 过 编 程 方 式 差 不 多 , 所 以 只 需 要 简 单 的 解 释 . 其 中 default-package 属性 用 来 定 义 一 个 默 认 的 包 路 径 , 如 果 下 面 指 定 的 class 不 是 全 限 定 名 称 的 话 , 会 自 动 加 上 这 个 默 认的 包 路 径 . 每 个 xml 文 件 都 可 以 包 含 任 意 多 个 bean 节 点 , 每 个 对 应 一 个 要 添 加 约 束 条 件 的 实 体 类 .42


映 射 约 束警 告每 个 实 体 类 只 能 在 所 有 的 xml 映 射 文 件 中 被 定 义 一 次 , 否 则 会 抛 出 异 常 .通 过 添 加 ignore-annotations 属 性 并 将 其 设 置 为 true 可 以 忽 略 在 对 应 bean 上 添 加 的 约 束 标 注 信息 , 这 个 属 性 的 默 认 值 就 是 true. ignore-annotations 属 性 还 可 以 定 义 在 class, fields 和getter 属 性 上 , 如 果 没 有 明 确 指 定 的 话 , 那 么 默 认 级 别 是 bean ( 可 参 考 第 2.1 节 “ 定 义 约 束 ”).constraint 节 点 用 于 添 加 一 个 约 束 条 件 到 其 父 节 点 对 应 的 元 素 上 , 并 且 它 需 要 通 过 annotation 属性 来 指 定 需 要 使 用 哪 个 约 束 条 件 . 对 于 每 个 约 束 条 件 中 所 需 要 的 属 性 , 其 中 , 由 Bean Validation规 范 规 定 的 属 性 (message, groups 和 payload) 可 以 通 过 同 名 的 子 节 点 来 定 义 , 而 每 个 约 束 条 件中 自 定 义 的 属 性 , 则 需 要 使 用 element 节 点 来 定 义 .class 节 点 同 样 支 持 通 过 group-sequence 节 点 来 对 一 个 类 的 默 认 校 验 组 进 行 重 定 义 ( 请 参 考第 2.3.2 节 “ 对 一 个 类 重 定 义 其 默 认 校 验 组 ”) .最 后 , 你 还 可 以 通 过 constraint-definition 节 点 来 对 一 个 指 定 的 约 束 条 件 上 绑 定 的 校 验 器(ConstraintValidator) 进 行 修 改 . 此 节 点 上 的 annotation 对 应 要 修 改 的 约 束 条 件 , 而validated-by 子 节 点 中 ( 按 顺 序 ) 列 出 要 关 联 到 此 约 束 条 件 上 的 校 验 器 ( ConstraintValidator 的 实 现类 ), 而 include-existing-validator 属 性 如 果 是 false 的 话 , 那 么 默 认 定 义 在 此 约 束 条 件 上 的 校 验器 将 被 忽 略 , 如 果 为 true, 那 么 在 xml 中 定 义 的 校 验 器 会 被 添 加 在 约 束 条 件 上 默 认 定 义 的 校 验 器 的后 面 .43


第 5Bootstrapping在 第 5.1 节 “Configuration 和 ValidatorFactory” 中 我 们 说 道 过 , 最 简 单 的 创 建一 个 Validator 实 例 的 方 法 是 通 过 Validation.buildDefaultValidatorFactory. 在 本 章 中 我 们 会 继 续 介绍 javax.validation.Validation 中 的 其 他 方 法 , 以 及 如 何 通 过 这 些 方 法 在 Bean Validation 初 始 化 的时 候 对 其 进 行 配 置 的 .The different bootstrapping options allow, amongst other things, to bootstrapany Bean Validation implementation on the classpath. 通 常 , 一 个 服 务 的提 供 者 是 能 够 被 Java Service Provider [http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider] 发 现 的 . 对 于 Bean Validation 的 实 现 ( 服 务 提 供 者 ) 来 说 , 他 们的 META-INF/services 目 录 下 需 要 包 含 一 个 名 为 javax.validation.spi.ValidationProvider 的 文 件 . 此文 件 中 包 含 了 一 个 ValidationProvider 接 口 的 实 现 类 的 全 路 径 名 称 , 具 体 到 Hibernate Validator 来说 , 就 是 org.hibernate.validator.HibernateValidator.注 意如 果 当 前 类 路 径 下 存 在 多 个 Bean Validation 的 实 现 , 那么 Validation.buildDefaultValidatorFactory() 并 不 能 保 证 具 体 那 个 实 现 会 被 使 用 .如 果 想 指 定 某 一 个 的 话 , 请 使 用 Validation.byProvider().5.1. Configuration 和 ValidatorFactoryValidation 类 提 供 了 三 种 方 法 来 创 建 一 个 Validator 的 实 例 , 例 5.1“Validation.buildDefaultValidatorFactory()” 中 显 示 的 是 最 简 单 的 方 法 .例 5.1. Validation.buildDefaultValidatorFactory()ValidatorFactory factory = Validation.buildDefaultValidatorFactory();Validator validator = factory.getValidator();你 也 可 以 通 过 Validation.byDefaultProvider() 现 获 取 一 个 Configuration 对 象 , 这 样 可 以 对 要 创 建的 Validator 进 行 配 置 .例 5.2. Validation.byDefaultProvider()Configuration config = Validation.byDefaultProvider().configure();config.messageInterpolator(new MyMessageInterpolator()).traversableResolver( new MyTraversableResolver()).constraintValidatorFactory(new MyConstraintValidatorFactory());ValidatorFactory factory = config.buildValidatorFactory();Validator validator = factory.getValidator();45


第 5 章 BootstrappingMessageInterpolator, TraversableResolver 和 ConstraintValidatorFactory 会 在 后 面 详 细 介 绍 .最 后 , 你 可 以 指 定 使 用 哪 个 Bean Validation 的 实 现 . 如 果 类 路 径 下 存 在 多 个 Bean Validation的 实 现 的 话 , 这 样 就 很 有 必 要 了 . 例 如 , 如 果 你 想 使 用 Hibernate Validator 来 作 为 内 部 实 现 来 创建 Validator 的 话 :例 5.3. Validation.byProvider( HibernateValidator.class )HibernateValidatorConfiguration config = Validation.byProvider( HibernateValidator.class ).configure();config.messageInterpolator(new MyMessageInterpolator()).traversableResolver( new MyTraversableResolver()).constraintValidatorFactory(new MyConstraintValidatorFactory());ValidatorFactory factory = config.buildValidatorFactory();Validator validator = factory.getValidator();提 示创 建 出 来 的 Validator 实 例 是 线 程 安 全 的 , 所 以 你 可 以 把 它 缓 存 起 来 .5.2. ValidationProviderResolver如 果 Java Service Provider 机 制 在 你 的 环 境 中 不 能 够 正 常 工 作 , 或 者 你 有 特 别 的 classloader设 置 的 话 , 你 也 可 以 提 供 一 个 自 定 义 的 ValidationProviderResolver. 例 5.4 “ 使 用 自 定 义 的ValidationProviderResolver” 显 示 了 如 何 在 OSGi 环 境 中 插 入 自 定 义 的 provider resolver.例 5.4. 使 用 自 定 义 的 ValidationProviderResolverConfiguration config = Validation.byDefaultProvider().providerResolver( new OSGiServiceDiscoverer() ).configure();ValidatorFactory factory = config.buildValidatorFactory();Validator validator = factory.getValidator();在 这 种 情 况 下 , 你 的 OSGiServiceDiscoverer 类 需 要 实 现 ValidationProviderResolver 接 口 :例 5.5. ValidationProviderResolver 接 口public interface ValidationProviderResolver {/*** Returns a list of ValidationProviders available in the runtime environment.** @return list of validation providers.*/List


MessageInterpolator}5.3. MessageInterpolator第 2.2.4 节 “ 验 证 失 败 提 示 信 息 解 析 ” already discussed the default message interpolationalgorithm. If you have special requirements for your message interpolation you canprovide a custom interpolator using Configuration.messageInterpolator(). This messageinterpolator will be shared by all validators generated by the ValidatorFactory createdfrom this Configuration. 例 5.6 “ 自 定 义 的 MessageInterpolator” shows an interpolator(available in Hibernate Validator) which can interpolate the value being validated inthe constraint message. To refer to this value in the constraint message you can use:•${validatedValue}: this will call String.valueOf on the validated value.•${validatedValue:}: provide your own format string which will be passed toString.format together with the validated value. Refer to the javadoc of String.formatfor more information about the format options.例 5.6. 自 定 义 的 MessageInterpolatorConfiguration configuration = Validation.byDefaultProvider().configure();ValidatorFactory factory = configuration.messageInterpolator(new ValueFormatterMessageInterpolator(configuration.getDefaultMessageInterpolator())).buildValidatorFactory();Validator validator = factory.getValidator();提 示It is recommended that MessageInterpolator implementations delegatefinal interpolation to the Bean Validation default MessageInterpolatorto ensure standard Bean Validation interpolation rules arefollowed. The default implementation is accessible throughConfiguration.getDefaultMessageInterpolator().5.3.1. ResourceBundleLocator一 个 普 遍 的 需 求 是 你 可 能 需 要 为 错 误 消 息 解 析 指 定 你 自 己 的 resource bundles.ResourceBundleMessageInterpolator 是 Hibernate Validator 中 默 认 的 MessageInterpolator 的 实现 , 它 默 认 情 况 下 是 通 过 ResourceBundle.getBundle 来 获 取 resource bundle 的 . 不过 , ResourceBundleMessageInterpolator 也 支 持 你 指 定 一 个 自 定 义 的 ResourceBundleLocator 实 现 来 提供 你 自 己 的 resource bundle. 例 5.7 “ 自 定 义 的 ResourceBundleLocator” 提 供 了 一 个 示 例 . 在这 个 例 子 中 , 先 通 过 HibernateValidatorConfiguration.getDefaultResourceBundleLocator 获 取 默 认的 ResourceBundleLocator 实 现 , 然 后 再 用 你 自 定 义 的 实 现 把 默 认 的 包 装 起 来 , 代 理 模 式 .47


第 5 章 Bootstrapping例 5.7. 自 定 义 的 ResourceBundleLocatorHibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();ResourceBundleLocator defaultResourceBundleLocator = configure.getDefaultResourceBundleLocator();ResourceBundleLocator myResourceBundleLocator = new MyCustomResourceBundleLocator(defaultResourceBundleLocator);configure.messageInterpolator(new ResourceBundleMessageInterpolator(myResourceBundleLocator));Hibernate Validator 提 供 了 两 个 ResourceBundleLocator 的 实 现 - PlatformResourceBundleLocator( 默 认 ) 和 AggregateResourceBundleLocator. 后 者 可 以 定 义 一 系 列 的 resource bundle, 然 后 它 会读 取 这 些 文 件 , 并 且 把 它 们 组 合 成 一 个 . 更 多 信 息 请 参 考 此 类 的 javadoc 文 档 .5.4. TraversableResolver到 目 前 位 置 我 们 还 没 有 讨 论 过 TraversableResolver 接 口 , 它 的 设 计 目 的 是 在 某 些 情 况 下 , 我 们 可 能不 应 该 去 获 取 一 个 属 性 的 状 态 . 最 典 型 的 情 况 就 是 一 个 延 迟 加 载 的 属 性 或 者 与 JPA 中 涉 及 到 关 联 关系 的 时 候 . 当 验 证 这 两 种 情 况 的 属 性 的 时 候 , 很 可 能 会 触 发 一 次 对 数 据 库 的 查 询 .Bean Validation正 是 通 过 TraversableResolver 接 口 来 控 制 能 否 访 问 某 一 个 属 性 的 ( 例 5.8 “TraversableResolver接 口 ”).例 5.8. TraversableResolver 接 口/*** Contract determining if a property can be accessed by the Bean Validation provider* This contract is called for each property that is being either validated or cascaded.** A traversable resolver implementation must be thread-safe.**/public interface TraversableResolver {/*** Determine if the Bean Validation provider is allowed to reach the property state** @param traversableObject object hosting traversableProperty or null* if validateValue is called* @param traversableProperty the traversable property.* @param rootBeanType type of the root object passed to the Validator.* @param pathToTraversableObject path from the root object to* traversableObject* (using the path specification defined by Bean Validator).* @param elementType either FIELD or METHOD.** @return true if the Bean Validation provider is allowed to* reach the property state, false otherwise.*/boolean isReachable(Object traversableObject,Path.Node traversableProperty,Class rootBeanType,Path pathToTraversableObject,ElementType elementType);48


ConstraintValidatorFactory}/*** Determine if the Bean Validation provider is allowed to cascade validation on* the bean instance returned by the property value* marked as @Valid.* Note that this method is called only if isReachable returns true for the same set of* arguments and if the property is marked as @Valid** @param traversableObject object hosting traversableProperty or null* if validateValue is called* @param traversableProperty the traversable property.* @param rootBeanType type of the root object passed to the Validator.* @param pathToTraversableObject path from the root object to* traversableObject* (using the path specification defined by Bean Validator).* @param elementType either FIELD or METHOD.** @return true if the Bean Validation provider is allowed to* cascade validation, false otherwise.*/boolean isCascadable(Object traversableObject,Path.Node traversableProperty,Class rootBeanType,Path pathToTraversableObject,ElementType elementType);Hibernate Validator provides two TraversableResolvers out of the box which will be enabledautomatically depending on your environment. The first is the DefaultTraversableResolverwhich will always return true for isReachable() and isTraversable(). The second isthe JPATraversableResolver which gets enabled when Hibernate Validator gets used incombination with JPA 2. In case you have to provide your own resolver you can do soagain using the Configuration object as seen in 例 5.9 “ 自 定 义 的 TraversableResolver”.例 5.9. 自 定 义 的 TraversableResolverConfiguration configuration = Validation.byDefaultProvider().configure();ValidatorFactory factory = configuration.traversableResolver(new MyTraversableResolver()).buildValidatorFactory();Validator validator = factory.getValidator();5.5. ConstraintValidatorFactory最 后 , 还 有 个 配 置 项 得 提 一 下 , 那 就 是 ConstraintValidatorFactory 类 . Hibernate Validator 中 默认 的 ConstraintValidatorFactory 需 要 一 个 无 参 的 构 造 方 法 来 初 始 化 ConstraintValidator 的 实 例 ( 参考 第 3.1.2 节 “ 约 束 校 验 器 ”). 对 于 自 定 义 的 ConstraintValidatorFactory 实 现 来 说 , 例 如 , 你 可以 让 其 支 持 对 约 束 条 件 的 依 赖 注 入 等 功 能 . 配 置 使 用 这 个 自 定 义 的 ConstraintValidatorFactory 的方 法 还 是 老 样 子 ( 例 5.10 “ 自 定 义 的 ConstraintValidatorFactory”).49


第 5 章 Bootstrapping例 5.10. 自 定 义 的 ConstraintValidatorFactoryConfiguration configuration = Validation.byDefaultProvider().configure();ValidatorFactory factory = configuration.constraintValidatorFactory(new IOCConstraintValidatorFactory()).buildValidatorFactory();Validator validator = factory.getValidator();你 需 要 实 现 此 接 口 :例 5.11. ConstraintValidatorFactory 接 口public interface ConstraintValidatorFactory {/*** @param key The class of the constraint validator to instantiate.** @return A constraint validator instance of the specified class.*/


第 6Metadata APIThe Bean Validation specification provides not only a validation engine, but also ametadata repository for all defined constraints. The following paragraphs are discussingthis API. All the introduced classes can be found in the javax.validation.metadatapackage.6.1. BeanDescriptorThe entry into the metadata API is via Validator.getConstraintsForClass whichreturns an instance of the BeanDescriptor [http://docs.jboss.org/hibernate/stable/beanvalidation/api/javax/validation/metadata/BeanDescriptor.html] interface. Usingthis bean descriptor you can determine whether the specified class hosts any constraintsat all via beanDescriptor.isBeanConstrained.提 示If a constraint declaration hosted by the requested class is invalid, aValidationException is thrown.You can then call beanDescriptor.getConstraintDescriptors to get a set ofConstraintDescriptors representing all class level constraints.If you are interested in property level constraints, you can callbeanDescriptor.getConstraintsForProperty or beanDescriptor.getConstrainedProperties to get asingle resp. set of PropertyDescriptors (see 第 6.2 节 “PropertyDescriptor”).6.2. PropertyDescriptorThe PropertyDescriptor [http://docs.jboss.org/hibernate/stable/beanvalidation/api/javax/validation/metadata/PropertyDescriptor.html] interface extends theElementDescriptor interface and represents constraints on properties of a class. Theconstraint can be declared on the attribute itself or on the getter of the attribute -provided Java Bean naming conventions are respected. A PropertyDescriptor adds isCascaded(returning true if the property is marked with @Valid) and getPropertyName to theElementDescriptor functionality.6.3. ElementDescriptorThe ElementDiscriptor [http://docs.jboss.org/hibernate/stable/beanvalidation/api/javax/validation/metadata/ElementDescriptor.html] interface is the common base class forBeanDescriptor and PropertyDescriptor. Next to the hasConstraints and getConstraintDescriptorsmethods it also offers access to the ConstraintFinder API which allows you to query51


第 6 章 Metadata APIthe metadata API in a more fine grained way. For example you can restrict your searchto constraints described on fields or on getters or a given set of groups. Given anElementDescriptor instance you just call findConstraints to retrieve a ConstraintFinderinstance.例 6.1. Usage of ConstraintFinderValidator validator = Validation.buildDefaultValidatorFactory().getValidator();BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Person.class);PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty("name");Set


第 7与 其 他 框 架 集 成Hibernate Validator 的 设 计 初 衷 是 在 一 个 分 层 的 应 用 程 序 中 , 约 束 信 息 只 需 要 被 定 义 一 次 ( 通过 在 领 域 模 型 上 标 注 ), 然 后 在 不 同 的 层 中 进 行 数 据 校 验 .7.1. OSGiThe Hibernate Validator jar file is conform to the OSGi specification and can be usedwithin any OSGi container. The following lists represent the packages imported andexported by Hibernate Validator. The classes within the exported packages are consideredpart of Hibernate Validator public API.提 示The Java Service Provider mechanism used by Bean Validation toautomatically discover validation providers doesn't work in anOSGi environment. To solve this, you have to provide a customValidationProviderResolver. See 第 5.2 节 “ValidationProviderResolver”Exported packages•org.hibernate.validator•org.hibernate.validator.constraints•org.hibernate.validator.cfg•org.hibernate.validator.cfg.context•org.hibernate.validator.cfg.defs•org.hibernate.validator.group•org.hibernate.validator.messageinterpolation•org.hibernate.validator.method•org.hibernate.validator.method.metadata•org.hibernate.validator.resourceloadingImported packages•javax.persistence.*, [2.0.0,3.0.0), optional•javax.validation.*, [1.0.0,2.0.0)•javax.xml.*53


第 7 章 与 其 他 框 架 集 成•org.xml.sax.*•org.slf4j.*, [1.5.6,2.0.0)•org.joda.time.*, [1.6.0,2.0.0), optional•org.jsoup.*, [1.5.2,2.0.0), optional7.2. 与 数 据 库 集 成 校 验Hibernate Annotations ( 即 Hibernate 3.5.x) 会 自 动 的 把 你 定 已 在 实 体 模 型 上 的 约 束 信 息 添 加到 其 映 射 信 息 中 . 例 如 , 假 设 你 的 一 个 实 体 类 的 属 性 上 有 @NotNull 的 约 束 的 话 , 那 么 Hibernate 在生 成 创 建 此 实 体 对 应 的 表 的 DDL 的 时 候 , 会 自 动 的 给 那 个 属 性 对 应 的 字 段 添 加 上 not null.如 果 因 为 某 种 原 因 , 你 不 想 使 用 这 个 特 性 , 那 么 可 以 将 hibernate.validator.apply_to_ddl 属 性 设 置为 false. 请 参 考 ???.你 也 可 以 限 制 这 个 DDL 约 束 自 动 生 成 的 特 性 只 应 用 到 一 部 分 实 体 类 . 只 需 要 设 置org.hibernate.validator.group.ddl 属 性 , 这 个 属 性 的 值 是 你 想 要 应 用 此 特 性 的 实 体 类 的 全 路 径名 称 , 每 个 以 逗 号 分 隔 .7.3. ORM 集 成Hibernate Validator 不 仅 能 够 和 Hibernate 集 成 工 作 , 还 能 够 和 任 何 JPA 的 实 现 很 好 的 一 起 工 作 .提 示When lazy loaded associations are supposed to be validated it isrecommended to place the constraint on the getter of the association.Hibernate replaces lazy loaded associations with proxy instances whichget initialized/loaded when requested via the getter. If, in such a case,the constraint is placed on field level the actual proxy instance is usedwhich will lead to validation errors.7.3.1. 基 于 Hibernate 事 件 模 型 的 校 验Hibernate Annotations ( 即 Hibernate 3.5.x) 中 包 含 了 一 个 的Hibernate 事 件 监 听 器 ( 译 注 : 请 阅 读 Hibernate Core 文 档 了 解 Hibernate 的 事 件 模 型 )- org.hibernate.cfg.beanvalidation.BeanValidationEventListener [http://fisheye.jboss.org/browse/Hibernate/core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java] - 来 为 Hibernate Validator 服务 . 当 一 个 PreInsertEvent, PreUpdateEvent 或 PreDeleteEvent 事 件 发 生 的 时 候 , 这个 监 听 器 就 可 以 对 该 事 件 所 涉 及 到 的 实 体 对 象 进 行 校 验 , 如 果 校 验 不 通 过 的 话 , 则 抛 出 异常 . 默 认 情 况 下 , Hibernate 在 对 每 个 对 象 进 行 保 存 或 者 修 改 操 作 的 时 候 , 都 会 对 其 进 行 校验 , 而 删 除 操 作 则 不 会 . 你 可 以 通 过 javax.persistence.validation.group.pre-persist,javax.persistence.validation.group.pre-update和54


JPAjavax.persistence.validation.group.pre-remove 属 性 来 定 义 对 应 事 件 发 生 的 时 候 , 具 体 要 校 验哪 ( 些 ) 个 校 验 组 , 这 个 属 性 的 值 是 要 应 用 的 校 验 组 类 的 全 路 径 , 使 用 逗 号 分 隔 . 例 7.1 “ 自 定 义BeanValidationEvenListener” 显 示 了 这 几 个 属 性 在 Hibernate 内 部 定 义 的 默 认 值 , 所 以 , 你 不 需要 在 你 的 应 用 中 再 重 复 定 义 了 .如 果 发 生 了 违 反 约 束 条 件 的 情 况 , 该 监 听 器 会 抛 出 一 个 运 行 时 的 ConstraintViolationException 异常 , 此 异 常 包 含 了 一 系 列 的 ConstraintViolation 对 象 用 于 描 述 每 个 违 反 了 约 束 条 件 的 情 况 .如 果 类 路 径 上 有 Hibernate Validator, 则 Hibernate Annotations ( 或 Hibernate EntityManager)会 自 动 调 用 它 , 如 果 你 想 避 免 这 种 情 况 , 可 以 设 置 javax.persistence.validation.mode 属 性为 none.注 意如 果 实 体 模 型 上 没 有 定 义 约 束 条 件 , 则 不 会 有 任 何 性 能 损 耗 .如 果 你 想 在 Hibernate Core 中 使 用 上 面 提 到 的 事 件 监 听 器 , 则 可 以 在 hibernate.cfg.xml 中 定 义 如 下的 配 置 信 息 :例 7.1. 自 定 义 BeanValidationEvenListener...javax.validation.groups.Defaultjavax.validation.groups.Default...7.3.2. JPAIf you are using JPA 2 and Hibernate Validator is inthe classpath the JPA2 specification requires that Bean Validationgets enabled. The properties javax.persistence.validation.group.pre-persist,javax.persistence.validation.group.pre-updateand55


第 7 章 与 其 他 框 架 集 成javax.persistence.validation.group.pre-remove as described in 第 7.3.1 节 “ 基于 Hibernate 事 件 模 型 的 校 验 ” can in this case be configured in persistence.xml.persistence.xml also defines a node validation-mode which can be set to AUTO, CALLBACK,NONE. The default is AUTO.对 于 JPA1 来 讲 , 你 需 要 自 己 创 建 和 注 册 Hibernate Validator. 如 果 你 是 使 用 HibernateEntityManager, 那 么 你 可 以 把 第 7.3.1 节 “ 基 于 Hibernate 事 件 模 型 的 校 验 ” 中 列 出 来的 BeanValidationEventListener 类 添 加 到 你 的 项 目 中 , 然 后 再 手 工 注 册 它 .7.4. 展 示 层 校 验如 果 你 正 在 使 用 JSF2 或 者 <strong>JBoss</strong> Seam, 并 且 Hibernate Validator (Bean Validation) 在 类 路 径上 的 话 , 那 么 界 面 上 的 字 段 可 以 被 自 动 校 验 . 例 7.2 “ 在 JSF2 中 使 用 Bean Validation” 显 示 了一 个 在 JSF 页 面 上 使 用 f:validateBean 标 签 的 实 例 . 更 多 的 信 息 请 参 考 Seam 的 文 档 或 者 JSF2 规 范 .例 7.2. 在 JSF2 中 使 用 Bean Validation ... 提 示The integration between JSF 2 and Bean Validation is described in the"Bean Validation Integration" chapter of <strong>JSR</strong>-314 [http://jcp.org/en/jsr/detail?id=314]. It is interesting to know that JSF 2 implements a customMessageInterpolator to ensure ensure proper localization. To encouragethe use of the Bean Validation message facility, JSF 2 will per defaultonly display the generated Bean Validation message. This can, however, beconfigured via the application resource bundle by providing the followingconfiguration ({0} is replaced with the Bean Validation message and {1}is replaced with the JSF component label):javax.faces.validator.BeanValidator.MESSAGE={1}: {0}The default is:javax.faces.validator.BeanValidator.MESSAGE={0}56


第 8Hibernate Validator SpecificsIn the following sections we are having a closer look at some of the Hibernate Validatorspecific features (features which are not part of the Bean Validation specification).This includes the fail fast mode, the programmatic constraint configuration API andboolean composition of composing constraints.注 意The features described in the following sections are not portable betweenBean Validation providers/implementations.8.1. Public APILet's start, however, with a look at the public API of Hibernate Validator. 表 8.1“Hibernate Validator public API” lists all packages belonging to this API and describestheir purpose.Any packages not listed in that table are internal packages of Hibernate Validator andare not intended to be accessed by clients. The contents of these internal packagescan change from release to release without notice, thus possibly breaking any clientcode relying on it.注 意In the following table, when a package is public its not necessarily truefor its nested packages.表 8.1. Hibernate Validator public APIPackagesorg.hibernate.validatororg.hibernate.validator.cfg,org.hibernate.validator.cfg.context,org.hibernate.validator.cfg.defsDescriptionThis package contains the classes used bythe Bean Validation bootstrap mechanism(eg. validation provider, configurationclass). For more details see 第 5 章Bootstrapping.With Hibernate Validator you can defineconstraints via a fluent API. Thesepackages contain all classes neededto use this feature. In the packageorg.hibernate.validator.cfg you will findthe ConstraintMapping class and in57


第 8 章 Hibernate Validator Spe...Packagesorg.hibernate.validator.constraintsorg.hibernate.validator.groupDescriptionpackage org.hibernate.validator.cfg.defsall constraint definitions. For moredetails see 第 8.4 节 “Programmaticconstraint definition”.In addition to Bean Validationconstraints, Hibernate Validator providessome useful custom constraints. Thispackage contains all custom annotationclasses. For more details see 第 2.4.2 节“Additional constraints”.With Hibernate Validator you can definedynamic default group sequences infunction of the validated object state.This package contains all classes neededto use this feature (GroupSequenceProviderannotation and DefaultGroupSequenceProvidercontract). For more details see 第 2.3.2 节“ 对 一 个 类 重 定 义 其 默 认 校 验 组 ”.org.hibernate.validator.messageinterpolation, These packages contain the classes relatedorg.hibernate.validator.resourceloading to constraint message interpolation.The first package contains twoimplementations of MessageInterpolator. Thefirst one, ValueFormatterMessageInterpolatorallows to interpolate the validatedvalue into the constraint message,see 第 5.3 节 “MessageInterpolator”.The second implementation namedResourceBundleMessageInterpolator is theimplementation used by default byHibernate Validator. This implementationrelies on a ResourceBundleLocator, see第 5.3.1 节 “ResourceBundleLocator”.Hibernate Validator provides differentResourceBundleLocator implementationslocated in the packageorg.hibernate.validator.resourceloading.org.hibernate.validator.method,org.hibernate.validator.method.metadataHibernate Validator provides supportfor method-level constraints based onappendix C of the Bean Validationspecification. The first package containsthe MethodValidator interface allowing youto validate method return values and58


Fail fast modePackagesDescriptionparameters. The second package containsmeta data for constraints hosted onparameters and methods which can beretrieved via the MethodValidator.8.2. Fail fast modeFirst off, the fail fast mode. Hibernate Validator allows to return from the currentvalidation as soon as the first constraint violation occurs. This is called the failfast mode and can be useful for validation of large object graphs where one is onlyinterested whether there is a constraint violation or not. 例 8.1 “Enabling failFastvia a property”, 例 8.2 “Enabling failFast at the Configuration level” and 例 8.3“Enabling failFast at the ValidatorFactory level” show multiple ways to enable thefail fast mode.例 8.1. Enabling failFast via a propertyHibernateValidatorConfiguration configuration =Validation.byProvider( HibernateValidator.class ).configure();ValidatorFactory factory = configuration.addProperty( "hibernate.validator.fail_fast","true" ).buildValidatorFactory();Validator validator = factory.getValidator();// do some actual fail fast validation...例 8.2. Enabling failFast at the Configuration levelHibernateValidatorConfiguration configuration =Validation.byProvider( HibernateValidator.class ).configure();ValidatorFactory factory = configuration.failFast( true ).buildValidatorFactory();Validator validator = factory.getValidator();// do some actual fail fast validation...例 8.3. Enabling failFast at the ValidatorFactory levelHibernateValidatorConfiguration configuration =Validation.byProvider( HibernateValidator.class ).configure();ValidatorFactory factory = configuration.buildValidatorFactory();Validator validator = factory.getValidator();// do some non fail fast validation59


第 8 章 Hibernate Validator Spe......validator = factory.unwrap( HibernateValidatorFactory.class ).usingContext().failFast( true ).getValidator();// do fail fast validation...8.3. Method validationThe Bean Validation API allows to specify constraints for fields, properties and types.Hibernate Validator goes one step further and allows to place contraint annotationsalso on method parameters and method return values, thus enabling a programming styleknown as "Programming by Contract".More specifically this means that Bean Validation constraints can be used to specify•the preconditions that must be met before a method invocation (by annotating methodparameters with constraints) and•the postconditions that are guaranteed after a method invocation (by annotatingmethods)This approach has several advantages over traditional ways of parameter and returnvalue checking:•The checks don't have to be performed manually (e.g. by throwingIllegalArgumentExceptions or similar), resulting in less code to write and maintain.•A method's pre- and postconditions don't have to be expressed again in the method'sJavaDoc, since the constraint annotations will automatically be included in thegenerated JavaDoc. This avoids redundancy and reduces the chance of inconsistenciesbetween implementation and documentation.注 意Method validation was also considered to be included in the Bean ValidationAPI as defined by <strong>JSR</strong> <strong>303</strong>, but it didn't become part of the 1.0 version.A basic draft is outlined in appendix C of the specification, and theimplementation in Hibernate Validator is largely influenced by this draft.The feature is considered again for inclusion in BV 1.1.8.3.1. Defining method-level constraints例 8.4 “Using method-level constraints” demonstrates the definition of method-levelconstraints.60


Defining method-level constraints例 8.4. Using method-level constraintspublic class RentalStation {@NotNullpublic Car rentCar(@NotNull Customer customer, @NotNull @Future Date startDate, @Min(1)int durationInDays) {//...}}Here the following pre- and postconditions for the rentCar() method are declared:•The renting customer may not be null•The rental's start date must not be null and must be in the future•The rental duration must be at least one day•The returned Car instance may not be nullUsing the @Valid annotation it's also possible to define that a cascaded validation ofparameter or return value objects shall be performed. An example can be found in 例 8.5“Cascaded validation of method-level constraints”.例 8.5. Cascaded validation of method-level constraintspublic class RentalStation {}@Validpublic Set getRentalsByCustomer(@Valid Customer customer) {//...}Here all the constraints declared at the Customer type will be evaluated when validatingthe method parameter and all constraints declared at the returned Rental objects willbe evaluated when validating the method's return value.8.3.1.1. Using method constraints in type hierarchiesSpecial care must be taken when defining parameter constraints in inheritancehierarchies.When a method is overridden in sub-types method parameter constraints can only bedeclared at the base type. The reason for this restriction is that the preconditionsto be fulfilled by a type's client must not be strengthened in sub-types (which maynot even be known to the base type's client). Note that also if the base method doesn't61


第 8 章 Hibernate Validator Spe...declare any parameter constraints at all, no parameter constraints may be added inoverriding methods.The same restriction applies to interface methods: no parameter constraints may bedefined at the implementing method (or the same method declared in sub-interfaces).If a violation of this rule is detected by the validation engine, ajavax.validation.ConstraintDeclarationException will be thrown. In 例 8.6 “Illegalparameter constraint declarations” some examples for illegal parameter constraintsdeclarations are shown.例 8.6. Illegal parameter constraint declarationspublic class Car {public void drive(Person driver) { ... }}public class RentalCar extends Car {//not allowed, parameter constraint added in overriding methodpublic void drive(@NotNull Person driver) { ... }}public interface ICar {void drive(Person driver);}public class CarImpl implements ICar {//not allowed, parameter constraint added in implementation of interface methodpublic void drive(@NotNull Person driver) { ... }}This rule only applies to parameter constraints, return value constraints may be addedin sub-types without any restrictions as it is alright to strengthen the postconditionsguaranteed to a type's client.8.3.2. Evaluating method-level constraintsTo validate method-level constraints Hibernate Validator provides the interfaceorg.hibernate.validator.method.MethodValidator.As shown in 例 8.7 “The MethodValidator interface” this interface defines methodsfor the evaluation of parameter as well as return value constraints and for retrievingan extended type descriptor providing method constraint related meta data.62


Evaluating method-level constraints例 8.7. The MethodValidator interfacepublic interface MethodValidator { Set validateParameter(T object, Method method, ObjectparameterValue, int parameterIndex, Class... groups); Set validateAllParameters(T object, Method method, Object[]parameterValues, Class... groups); Set validateReturnValue(T object, Method method, ObjectreturnValue, Class... groups);}TypeDescriptor getConstraintsForType(Class clazz);To retrieve a method validator get hold of an instance of HV's javax.validation.Validatorimplementation and unwrap it to MethodValidator as shown in 例 8.8 “Retrieving aMethodValidator instance”.例 8.8. Retrieving a MethodValidator instanceMethodValidator methodValidator = Validation.byProvider( HibernateValidator.class ).configure().buildValidatorFactory().getValidator().unwrap( MethodValidator.class );The validation methods defined on MethodValidator each return aSet. The type MethodConstraintViolation (see 例 8.9 “TheMethodConstraintViolation type”) extends javax.validation.ConstraintViolation andprovides additional method level validation specific information such as the methodand index of the parameter which caused the constraint violation.例 8.9. The MethodConstraintViolation typepublic interface MethodConstraintViolation extends ConstraintViolation {public static enum Kind { PARAMETER, RETURN_VALUE }Method getMethod();Integer getParameterIndex();String getParameterName();}Kind getKind();63


第 8 章 Hibernate Validator Spe...注 意The method getParameterName() currently returns synthetic parameteridentifiers such as "arg0", "arg1" etc. In a future version of HibernateValidator support for specifying parameter identifiers might be added.Typically the validation of method-level constraints is not invoked manually butautomatically upon method invocation by an integration layer using AOP (aspectorientedprogramming) or similar method interception facilities such as the JDK'sjava.lang.reflect.Proxy API or CDI ("<strong>JSR</strong> 299: Contexts and Dependency Injection for theJava TM EE platform").If a parameter or return value constraint can't be validated sucessfully such anintegration layer typically will throw a MethodConstraintViolationException which similarto javax.validation.ConstraintViolationException contains a set with the occurred constraintviolations.提 示If you are using CDI you might be interested in the Seam Validation[http://seamframework.org/Seam3/ValidationModule] project. This Seammodule provides an interceptor which integrates the method validationfunctionality with CDI.8.3.3. Retrieving method-level constraint meta dataAs outlined in 第 6 章 Metadata API the Bean Validation API provides rich capabilitiesfor retrieving constraint related meta data. Hibernate Validator extends this API andallows to retrieve constraint meta data also for method-level constraints.例 8.10 “Retrieving meta data for method-level constraints” shows how to use thisextended API to retrieve constraint meta data for the rentCar() method from theRentalStation type.例 8.10. Retrieving meta data for method-level constraintsTypeDescriptor typeDescriptor = methodValidator.getConstraintsForType(RentalStation.class)//retrieve a descriptor for the rentCar() methodMethodDescriptor rentCarMethod = typeDescriptor.getConstraintsForMethod("rentCar",Customer.class, Date.class, int.class);assertEquals(rentCarMethod.getMethodName(), "rentCar");assertTrue(rentCarMethod.hasConstraints());assertFalse(rentCarMethod.isCascaded());64


Programmatic constraint definition//retrieve constraints from the return valueSet


第 8 章 Hibernate Validator Spe...type-safe fashion. Definition classes exist for all built-in constraints in theorg.hibernate.validator.cfg.defs package.For custom constraints you can either create your own definition classes extendingConstraintDef or you can use GenericConstraintDef as seen in 例 8.12 “Programmaticconstraint definition using createGeneric()”.例 8.12. Programmatic constraint definition using createGeneric()ConstraintMapping mapping = new ConstraintMapping();mapping.type( Car.class ).property( "licensePlate", FIELD ).constraint( new GenericConstraintDef( CheckCase.class ).param( "value",CaseMode.UPPER ) );Not only standard class- and property-level constraints but also method constraintscan be configured using the API. As shown in 例 8.13 “Programmatic definition ofmethod constraints” methods are identified by their name and their parameters (ifthere are any). Having selected a method, constraints can be placed on the method'sparameters and/or return value.例 8.13. Programmatic definition of method constraintsConstraintMapping mapping = new ConstraintMapping();mapping.type( Car.class ).method( "drive", String.class, Integer.class ).parameter( 0 ).constraint( new NotNullDef() ).constraint( new MinDef().value ( 1 ) ).parameter( 1 ).constraint( new NotNullDef() ).returnValue().constraint( new NotNullDef() ).method( "check" ).returnValue().constraint( new NotNullDef() );Using the API it's also possible to mark properties, method parameters and methodreturn values as cascading (equivalent to annotating them with @Valid). An example canbe found in 例 8.14 “Marking constraints for cascaded validation”.例 8.14. Marking constraints for cascaded validationConstraintMapping mapping = new ConstraintMapping();mapping.type( Car.class ).property( "manufacturer", FIELD ).valid().property( "licensePlate", METHOD )66


Boolean composition for constraint composition.valid().method( "drive", String.class, Integer.class ).parameter( 0 ).valid().parameter( 1 ).valid().returnValue().valid().type( RentalCar.class ).property( "rentalStation", METHOD ).valid();Last but not least you can configure the default group sequence or the default groupsequence provider of a type as shown in 例 8.15 “Configuration of default groupsequence and default group sequence provider”.例 8.15. Configuration of default group sequence and default groupsequence providerConstraintMapping mapping = new ConstraintMapping();mapping.type( Car.class ).defaultGroupSequence( Car.class, CarChecks.class ).type( RentalCar.class ).defaultGroupSequenceProvider( RentalCarGroupSequenceProvider.class );Once a ConstraintMapping is set up it has to be passed to the configuration. Since theprogrammatic API is not part of the official Bean Validation specification you needto get hold of a HibernateValidatorConfiguration instance as shown in 例 8.16 “Creatinga Hibernate Validator specific configuration”.例 8.16. Creating a Hibernate Validator specific configurationConstraintMapping mapping = new ConstraintMapping();// configure mapping instanceHibernateValidatorConfiguration config =Validation.byProvider( HibernateValidator.class ).configure();config.addMapping( mapping );ValidatorFactory factory = config.buildValidatorFactory();Validator validator = factory.getValidator();8.5. Boolean composition for constraint compositionAs per Bean Validation specification the constraints of a composed constraint (see第 3.2 节 “ 约 束 条 件 组 合 ”) are all combined via a logical AND. This means all of thecomposing constraints need to return true in order for an overall successful validation.Hibernate Validator offers an extension to this logical AND combination which allows67


第 8 章 Hibernate Validator Spe...you to compose constraints via a logical OR or NOT. To do so you have to use theConstraintComposition annotation and the enum CompositionType with its values AND, OR andALL_FALSE. 例 8.17 “OR composition of constraints” shows how to build a composingconstraint where only one of the constraints has to be successful in order to passthe validation. Either the validated string is all lowercased or it is between twoand three characters long.例 8.17. OR composition of constraints@ConstraintComposition(OR)@Pattern(regexp = "[a-z]")@Size(min = 2, max = 3)@ReportAsSingleViolation@Target({ METHOD, FIELD })@Retention(RUNTIME)@Constraint(validatedBy = { })public @interface PatternOrSize {public abstract String message() default "{PatternOrSize.message}";public abstract Class[] groups() default { };public abstract Class


第 9Annotation Processor你 碰 到 过 下 面 这 些 让 人 抓 狂 的 情 况 么 :•specifying constraint annotations at unsupported data types (e.g. by annotating aString with @Past)• 对 一 个 JavaBean 的 setter 方 法 进 行 标 注 ( 而 不 是 getter)• 对 一 个 静 态 的 变 量 或 者 方 法 进 行 约 束 条 件 标 注 ( 这 样 是 不 支 持 滴 )这 样 的 话 , 你 就 应 该 看 看 Hibernate Validator 的 约 束 处 理 器 了 . 它 会 被 插 入 到 编 译 过 程 中 , 然后 如 果 发 现 如 果 哪 个 约 束 标 注 用 错 了 的 话 , 则 汇 报 编 译 错 误 .注 意You can find the Hibernate Validator Annotation Processor as part ofthe distribution bundle on Sourceforge [http://sourceforge.net/projects/hibernate/files/hibernate-validator] or in the <strong>JBoss</strong> Maven Repository(see 例 1.1 “Configuring the <strong>JBoss</strong> Maven repository”) under the GAVorg.hibernate:hibernate-validator-annotation-processor.9.1. 前 提 条 件Hibernate Validator 的 标 注 处 理 器 是 基 于 <strong>JSR</strong> 269 [http://jcp.org/en/jsr/detail?id=269] 所定 义 的 " 可 插 入 式 标 注 处 理 API" 的 . 这 个 API 从 Java 6 开 始 已 经 是 Java 平 台 的 一 部 分 了 , 所 以 请 确保 使 用 这 个 或 者 以 后 的 版 本 .9.2. 特 性As of Hibernate Validator 4.2.0.Final the Hibernate Validator Annotation Processorchecks that:• 应 用 了 约 束 标 注 的 属 性 的 类 型 是 否 被 该 约 束 所 支 持•only non-static fields or methods are annotated with constraint annotations•only non-primitive fields or methods are annotated with @Valid•only such methods are annotated with constraint annotations which are valid JavaBeansgetter methods (optionally, see below)•only such annotation types are annotated with constraint annotations which areconstraint annotations themselves•definition of dynamic default group sequence with @GroupSequenceProvider is valid69


第 9 章 Annotation Processor9.3. 配 置 项Hibernate Validator 标 注 处 理 器 的 行 为 可 以 通 过 表 9.1 “Hibernate Validator 标 注 处 理 器 配 置项 ” 中 列 出 的 处 理 器 配 置 项 [http://java.sun.com/javase/6/docs/technotes/tools/windows/javac.html#options] 加 以 控 制 .表 9.1. Hibernate Validator 标 注 处 理 器 配 置 项配 置 项功 能diagnosticKind 控 制 编 译 错 误 级 别 . 必 须 是 枚 举 类型 javax.tools.Diagnostic.Kind 中 的 值 ( 字 符 串形 式 ), 例 如 WARNING. 如 果 是 ERROR 的 话 , 那 么如 果 API 检 测 到 约 束 信 息 应 用 的 错 误 的 话 , 会 让编 译 过 程 终 止 , 默 认 是 ERROR.methodConstraintsSupportedverboseControls whether constraints are allowedat methods of any kind. Must be set to truewhen working with method level constraintsas supported by Hibernate Validator. Canbe set to false to allow constraints onlyat JavaBeans getter methods as defined bythe Bean Validation API. Defaults to true.Controls whether detailed processinginformation shall be displayed or not,useful for debugging purposes. Must beeither true or false. Defaults to false.9.4. 使 用 标 注 处 理 器本 小 节 详 细 介 绍 了 如 何 把 Hibernate Validator 标 注 处 理 器 与 命 令 行 编 译 (javac, Ant, Maven) 以及 IDE (Eclipse, IntelliJ IDEA, NetBeans) 集 成 .9.4.1. 命 令 行 编 译9.4.1.1. javac当 使 用 命 令 行 (javac [http://java.sun.com/javase/6/docs/technotes/guides/javac/index.html]) 编 译 的 时 候 , 通 过 "processorpath" 属 性 指 定 下 列 jar:•validation-api-1.0.0.GA.jar•hibernate-validator-4.2.0.Final.jar•hibernate-validator-annotation-processor-4.2.0.Final.jar下 面 显 示 了 一 个 具 体 的 示 例 . 这 样 , 标 注 处 理 器 就 会 自 动 被 编 译 器 检 测 到 并 且 调 用 .70


命 令 行 编 译例 9.1. 在 javac 中 使 用 标 注 处 理 器9.4.1.2. Apache Ant和 直 接 使 用 javac 差 不 多 , 可 以 在 Apache Ant [http://ant.apache.org/] 的 javac task [http://ant.apache.org/manual/CoreTasks/javac.html] 中 添 加 上 面 例 子 中 的 参 数 :例 9.2. 在 Ant 中 使 用 标 注 处 理 器javac src/main/java/org/hibernate/validator/ap/demo/Car.java \-cp /path/to/validation-api-1.0.0.GA.jar \-processorpath /path/to/validation-api-1.0.0.GA.jar:/path/to/hibernatevalidator-4.2.0.Final:/path/to/hibernate-validator-annotation-processor-4.2.0.Final.jar9.4.1.3. Maven对 于 和 Apache Maven [http://maven.apache.org/] 集 成 来 说 我 们 有 很 多 选 择 , 通 常 , 我 们 可 以 把Hibenrate Validator 标 注 处 理 器 作 为 依 赖 添 加 到 你 的 项 目 当 中 :例 9.3. 添 加 HV 标 注 处 理 器 为 依 赖...org.hibernatehibernate-validator-annotation-processor4.2.0.Finalcompile...这 样 , 这 个 处 理 器 就 能 够 自 动 的 被 编 译 器 所 调 用 . 虽 然 基 本 上 能 工 作 , 但 是 还 是 有 一 些 缺 点 , 在某 些 情 况 下 , 标 注 处 理 器 的 输 出 信 息 可 能 不 能 够 被 显 示 出 来 . ( 请 参 考 MCOMPILER-66 [http://jira.codehaus.org/browse/MCOMPILER-66]).另 外 的 一 个 选 择 是 使 用 Maven Annotation Plugin [http://code.google.com/p/mavenannotation-plugin/].不 过 在 此 文 档 撰 写 的 时 候 , 这 个 插 件 还 没 有 被 上 传 到 任 何 一 个 广 泛 被 使 用的 仓 库 中 . 所 以 , 你 需 要 自 己 把 这 个 插 件 自 己 的 仓 库 添 加 到 你 的 settings.xml 或 pom.xml 中 :71


第 9 章 Annotation Processor例 9.4. 添 加 Maven Annotation Plugin 的 仓 库...maven-annotation-plugin-repohttp://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo...现 在 , 禁 用 compiler 插 件 所 调 用 的 标 准 的 标 注 处 理 过 程 , 然 后 再 通 过 定 义 一 个 execution 来 配 置annotation plugin 的 运 行 , 还 需 要 把 Hibernate Validator 标 注 处 理 器 作 为 该 插 件 的 依 赖 添 加 进去 ( 这 样 , 此 标 注 处 理 器 就 不 会 被 当 成 你 的 项 目 的 依 赖 而 出 现 在 类 路 径 中 了 ):例 9.5. 配 置 Maven Annotation Plugin...maven-compiler-plugin1.61.6-proc:noneorg.bsc.mavenmaven-processor-plugin1.3.4processprocessprocess-sourcesorg.hibernatehibernate-validator-annotation-processor4.2.0.Finalcompile...72


IDE 集 成9.4.2. IDE 集 成9.4.2.1. Eclipse请 参 考 以 下 步 骤 来 在 Eclipse [http://www.eclipse.org/] 中 使 用 标 注 处 理 器 :• 右 键 点 击 你 的 项 目 , 然 后 选 择 " 属 性 "• 在 "Java Compiler" 页 面 确 认 " 编 译 级 别 " 设 置 的 是 "1.6". 否 则 的 话 是 无 法 使 用 标 注 处 理 器 的 .• 到 "Java Compiler" 下 面 的 "Annotation Processing" 页 面 , 然 后 选 择 " 启 用 标 注 处 理 "( 译 注 : 我的 电 脑 是 英 文 版 的 , 所 以 真 的 不 知 道 中 文 版 的 eclipse 上 , 这 些 翻 译 成 了 什 么 :(• 到 "Java Compiler - Annotation Processing - Factory Path" 页 面 , 然 后 添 加 下 面 的 jar 文 件 :•validation-api-1.0.0.GA.jar•hibernate-validator-4.2.0.Final.jar•hibernate-validator-annotation-processor-4.2.0.Final.jar• 确 认 工 作 空 间 重 新 编 译现 在 你 应 该 能 够 看 到 所 有 的 标 注 错 误 都 在 编 辑 窗 口 中 显 示 出 了 错 误 标 记 , 也 都 显 示 在 了 " 问 题 " 视图 :73


第 9 章 Annotation Processor9.4.2.2. IntelliJ IDEA请 参 考 以 下 步 骤 来 在 IntelliJ IDEA [http://www.jetbrains.com/idea/] (9.0 及 以 上 ): 中 使 用 标注 处 理 器 :• 选 择 "File", 然 后 "Settings",• 展 开 "Compiler" 节 点 , 然 后 点 击 "Annotation Processors"•Choose "Enable annotation processing" and enter the followingas "Processor path": /path/to/validation-api-1.0.0.GA.jar:/path/to/hibernatevalidator-4.2.0.Final.jar:/path/to/hibernate-validator-annotationprocessor-4.2.0.Final.jar• 添 加 处 理 器 的 全 路 径 名称 org.hibernate.validator.ap.ConstraintValidationProcessor 到 "Annotation Processors" 列 表• 如 果 需 要 的 话 , 添 加 你 的 模 块 到 "Processed Modules" 列 表重 新 编 译 你 的 项 目 , 然 后 应 该 能 看 到 关 于 约 束 标 注 的 错 误 信 息 了 :9.4.2.3. NetBeans从 6.9 这 个 版 本 开 始 , NetBeans [http://www.netbeans.org/] 也 支 持 标 注 处 理 了 . 可 以 通 过 下 面的 步 骤 来 启 用 它 :74


已 知 问 题• 右 键 点 击 你 的 项 目 , 然 后 选 择 " 属 性 "•Go to "Libraries", tab "Processor", and add the following JARs:•validation-api-1.0.0.GA.jar•hibernate-validator-4.2.0.Final.jar•hibernate-validator-annotation-processor-4.2.0.Final.jar• 到 "Build - Compiling" 页 面 选 中 "Enable Annotation Processing" 和"Enable Annotation Processing in Editor", 并 且 指 定 标 注 处 理 器 的 全 路 径 名称 org.hibernate.validator.ap.ConstraintValidationProcessor.所 有 的 约 束 标 注 问 题 应 该 都 会 在 编 辑 器 里 面 直 接 被 标 记 出 来 了 :9.5. 已 知 问 题以 下 是 截 止 到 2010 年 五 月 我 们 发 现 ( 但 尚 未 解 决 ) 的 问 题 :•HV-308 [http://opensource.atlassian.com/projects/hibernate/browse/HV-308]:Additional validators registered for a constraint using XML [http://docs.jboss.org/hibernate/stable/validator/reference/en/html_single/#d0e1957] are not evaluated bythe annotation processor.75


第 9 章 Annotation Processor• 有 时 候 , 在 eclipse 里 面 自 定 义 的 约 束 条 件 不 能 够 被 正 确 的 检 查 [http://opensource.atlassian.com/projects/hibernate/browse/HV-293]. 清 理 这 个 项 目 可 能 会 有 帮助 . 这 可 能 是 因 为 Eclipse 中 对 <strong>JSR</strong> 269 API 的 实 现 有 问 题 , 但 是 还 需 要 进 一 步 的 研 究 .•When using the processor within Eclipse, the check of dynamic default group sequencedefinitions doesn't work. After further investigation, it seems to be an issue withthe Eclipse <strong>JSR</strong> 269 API implementation.76


第 10进 一 步 阅 读Last but not least, a few pointers to further information.A great source for examples is the Bean Validation TCK which is available for anonymousaccess on GitHub [https://github.com/beanvalidation/beanvalidation-tck]. In particularthe TCK's tests [https://github.com/beanvalidation/beanvalidation-tck/tree/master/src/main/java/org/hibernate/jsr<strong>303</strong>/tck/tests] might be of interest. The <strong>JSR</strong> <strong>303</strong> [http://jcp.org/en/jsr/detail?id=<strong>303</strong>] specification itself is also a great way to deepen yourunderstanding of Bean Validation resp. Hibernate Validator.如 果 你 还 有 什 么 关 于 Hibernate Validator 的 问 题 或 者 想 和 我 们 分 享 你 的 使 用 经 验 , 请 使用 Hibernate Validator Wiki [http://community.jboss.org/en/hibernate/validator] 或 者去 Hibernate Validator Forum [https://forum.hibernate.org/viewforum.php?f=9] 发 帖 子 ( 译注 : 但 是 不 要 灌 水 :-D).如 果 你 发 现 了 Hibernate Validator 的 bug, 请 在 Hibernate's Jira [http://opensource.atlassian.com/projects/hibernate/browse/HV] 中 报 告 , 我 们 非 常 欢 迎 您 的 反 馈 !77

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!