1. 程式人生 > >Hibernate-validator校驗框架

Hibernate-validator校驗框架

1. 前言

Validator開發者使用手冊,適用後臺校驗功能的開發參考。文件中提到的功能,都是經過嚴謹測試驗證過的,保證可用,請開發人員放心設計和開發。

1.1. 背景

在我們日常後臺的開發中,涉及到很多的欄位驗證,一般普通的驗證框架就基本上可以滿足日常的開發需求,但有一些情況,這些普通的驗證框架無法達到要求,例如,現在有一個Segment類,裡面有一個屬性deptCity,但這個類同時用於多個介面中,比如說用到選取可利用航班以及選取目的航班中,在選取可利用航班這個介面中,deptCity是必輸項,格式為[A-Z]{3},但是選取目的航班介面中,deptCity不是必輸項,這時候問題就出現了,我們必須對驗證的屬性進行分組,於是就產生了我們的Validator驗證框架。當然,我們的驗證框架不僅僅只提供了分組的功能,欲知後事,請看下文分解。

1.2. 聯絡我們

在開發中遇到的任何問題,或者需要技術支援的地方,可以聯絡我們:

[email protected]

2. 參考文件

《Hibernate Validator - 5.1.0 - 英文版.pdf》

3. 概覽

3.1. 工作模式和配置模式

Validator提供兩種工作模式:

1、普通模式

2、快速失敗返回模式

預設的工作模式為快速失敗返回模式,一旦發現校驗失敗項,立即返回。普通模式在測試時期可以使用,可以對全部的校驗項進行完整的校驗(校驗組序列,以及基於校驗組序列的其他配置無效),通過修改配置檔案中的校驗模式,從而實現工作模式的自由切換。開發人員無需關心其中的原理和過程。

兩種配置模式:Annotation和Xml檔案(此處略)。

推薦使用Annotation註解模式。

工作模式配置如下:

validator.fail_fast:快速失敗返回模式(只要有一個驗證失敗,則返回異常)

validator.normal:普通模式(會校驗完所有的屬性,然後返回所有的驗證失敗資訊)

3.2. 校驗功能

Validator校驗框架按照Bean Validation的規範,使用了Hibernate Validatior框架。當前通過測試驗證的可以支援的功能有:

1、內建註解校驗

2、物件圖級聯校驗

3、校驗組分組校驗

4、校驗組序列

5、自定義預設校驗組功能

6、自定義智慧預設校驗組功能

7、自定義校驗註解

8、類校驗——類屬性的關聯校驗

下面將首先給出各個場景的具體的開發方法,而後介紹各個功能的應用場景,供大家定位自己的需求適用於哪幾種場景的整合。

此外,框架還內嵌了一些非法校驗的功能,比如輸入的校驗物件為null,或者指定的物件的屬性值錯誤,都會自行丟擲異常。

4. Validator的使用方法

4.1. Validator的jar包

目前,Validator的版本為2.0.0,後面可能會涉及到版本的更新,具體版本請和開發人員聯絡。原始工程為Maven工程,使用的時候,只需要匯入Validator依賴的pom檔案以及進行簡單的配置即可。

  1. <dependency>

  2. <groupId>com.chhliu.common</groupId>

  3. <artifactId>validate</artifactId>

  4. <version>2.0.0</version>

  5. </dependency>

4.2. Validator的配置檔案

配置檔案可以從jar包裡面獲取,或者直接找開發人員獲取皆可,使用的時候,直接加入類路徑下即可。附件部分給出了Validator依賴的pom檔案。配置檔案如下:

  1. <bean id="baseValidator" class="com.chhliu.common.validator.BaseValidator">

  2. <property name="validatorMode">

  3. <!-- 校驗器的工作模式:

  4. validator.fail_fast:快速失敗返回模式(只要有一個驗證失敗,則返回異常)

  5. validator.normal:普通模式(會校驗完所有的屬性,然後返回所有的驗證失敗資訊)

  6. -->

  7. <value>validator.normal</value>

  8. </property>

  9. </bean>

4.3. Validator介面

使用Validator的校驗框架非常的簡單,只需要注入Validator介面即可:

  1. @Autowired

  2. private Validator validator;

4.4. Validator中的方法

Validator提供了6個校驗介面供開發人員使用:

  1. 1、<T> void validate(T object) throws Exception

  2. 用途:校驗一個物件的預設校驗組的屬性。

  3. 2、<T> void validate(T object, Class<?>... groups) throws Exception

  4. 用途:校驗一個物件的指定的一個或多個校驗組的屬性。

  5. 3、<T> void validateProperty(T object, String propertyName) throws Exception

  6. 用途:校驗一個物件的預設校驗組的一個指定的屬性值。

  7. 4、<T> void validateProperty(T object, String propertyName, Class<?>... groups) throws Exception

  8. 用途:校驗一個物件指定校驗組中的一個指定的屬性值。

  9. 5、<T> void validateValue(Class<T> beanType, String propertyName, Object value) throws Exception

  10. 用途:校驗一個value是否符合指定類的預設校驗組下的某一個屬性值。

  11. 6、<T> void validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) throws Exception

  12. 用途:校驗一個value是否符合指定類的指定校驗組下的某一個屬性值。

4.5. 測試類

大家可以從工程原始碼的測試目錄中,找到測試類,所有的測試類都可以正常執行,並加入了詳細的註釋說明。

5. 各個校驗功能的開發指南

本章節提供了各種校驗功能具體的開發方法,基本能夠滿足開發人員所有 的校驗需求。(所有的實體類省略了get和set方法)

5.1. 內建註解校驗

適用場景:簡單的單屬性的校驗。

內建的校驗註解共分為三種:Bean Validation內建的校驗註解和Hibernate Validator拓展的校驗註解以及框架自帶的校驗註解。推薦大家首先考慮使用這些註解,簡單易用。

使用方法:

  1. public class RangeModel {

  2. @Length(min=5, max=17)

  3. private String length;

  4. @Size(min=1, max=3)

  5. private String age;

  6. @Range(min=150,max=250)

  7. private int high;

5.1.1. Bean Validator內建的註解

Annotation

支援的資料型別

作用

Hibernate metadata impact

@AssertFalse

Boolean, boolean

判斷關聯屬性是否為布林值false

沒有

@AssertTrue

Boolean, boolean

Checks that the annotated element istrue.

沒有

@DecimalMax

BigDecimal, BigInteger, String, byte, short, int,long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of Number.

被註解的值必須不大於約束中指定的最大值. 這個約束的引數是一個通過BigDecimal定義的最大值的字串表示.

沒有

@DecimalMin

BigDecimal, BigInteger, String, byte, short, int,long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of Number.

被註解的值必須不小於約束中指定的最小值. 這個約束的引數是一個通過BigDecimal定義的最小值的字串表示.

沒有

@Digits(integer=, fraction=)

BigDecimal, BigInteger, String, byte, short, int,long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of Number.

校驗整數位數和小數位數

對應的資料庫表字段會被設定精度(precision)和準度(scale).

@Future

java.util.Date, java.util.Calendar; Additionally supported by HV, if the Joda Time date/time API is on the class path: any implementations of ReadablePartial and ReadableInstant.

檢查給定的日期是否比現在晚.

沒有

@Max

BigDecimal, BigInteger, byte, short, int, longand the respective wrappers of the primitive types. Additionally supported by HV: String(the numeric value represented by a String is evaluated), any sub-type of Number.

檢查該值是否小於或等於約束條件中指定的最大值.

會給對應的資料庫表字段新增一個check的約束條件.

@Min

BigDecimal, BigInteger, byte, short, int, longand the respective wrappers of the primitive types. Additionally supported by HV: String(the numeric value represented by a String is evaluated), any sub-type of Number.

檢查該值是否大於或等於約束條件中規定的最小值.

會給對應的資料庫表字段新增一個check的約束條件.

@NotNull

Any type

Checks that the annotated value is notnull.

對應的表字段不允許為null.

@Null

Any type

Checks that the annotated value is null.

沒有

@Past

java.util.Date, java.util.Calendar; Additionally supported by HV, if the Joda Time date/time API is on the class path: any implementations of ReadablePartial and ReadableInstant.

檢查註解物件中的值表示的日期比當前早.

沒有

@Pattern(regex=, flag=)

String

檢查該字串是否能夠在match指定的情況下被regex定義的正則表示式匹配.

沒有

@Size(min=, max=)

String, Collection, Map and arrays

校驗物件的size。本文作者認為前提是該物件有size()方法,String除外。

對應的資料庫表字段的長度會被設定成約束中定義的最大值.

@Valid

Any non-primitive type

遞迴的對關聯物件進行校驗, 如果關聯物件是個集合或者陣列, 那麼對其中的元素進行遞迴校驗,如果是一個map,則對其中的值部分進行校驗.

沒有

5.1.2. Hibernate Validator拓展的註解

Annotation

支援的資料型別

作用

Hibernate metadata impact

@CreditCardNumber

String

校驗信用卡號碼

沒有

@Email

String

校驗郵件地址

沒有

@Length(min=, max=)

String

功能同@Size,但是隻支援String型別

對應的資料庫表字段的長度會被設定成約束中定義的最大值.

@NotBlank

String

不為null,不為空值,不為全空格。功能強大於@NotEmpty

沒有

@NotEmpty

String,Collection,Map and arrays

校驗是否為null或者為空值。功能強於@NotNull

沒有

@Range(min=, max=)

BigDecimal,BigInteger,String, byte,short, int,long and the respective wrappers of the primitive types

判斷數值的範圍,不僅支援數值型別,還支援字串、位元組等等型別

沒有

@SafeHtml(whitelistType=, additionalTags=)

CharSequence

無使用價值

沒有

@ScriptAssert(lang=, script=, alias=)

Any type

無使用價值

沒有

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

String

Checks if the annotated string is a valid URL according to RFC2396. If any of the optional parameters protocol, host or port are specified, the corresponding URL fragments must match the specified values. The optional parametersregexp and flags allow to specify an additional regular expression (including regular expression flags) which the URL must match.

沒有

5.1.3. Validator框架拓展註解

Annotation

支援的資料型別

作用

@NotEmptyPattern

String

在字串不為空的情況下,驗證是否匹配正則表示式

@ListStringPattern

List<String>

驗證集合中的字串是否滿足正則表示式

@DateValidator

String

驗證日期格式是否滿足正則表示式,Local為ENGLISH

@DateFormatCheckPattern

String

驗證日期格式是否滿足正則表示式,Local為自己手動指定

5.2. 物件圖級聯校驗

Validator不僅能夠校驗單個例項物件,還可以校驗完整的物件圖。對於例項中的物件成員屬性,註解上@Valid,就可以被關聯校驗。

  1. public class Order {

  2. @NotEmpty

  3. private String orderName;

  4. @NotNull

  5. @Valid

  6. private OrderItem orderItem;

  7. }

  8. public class OrderItem {

  9. @NotEmpty

  10. private String itemName;

  11. @NotEmpty

  12. @Pattern(regexp="\\d+")

  13. private String itemCode;

  14. @NotEmpty

  15. @Email

  16. private String email;

  17. }

  18. @RunWith(SpringJUnit4ClassRunner.class)

  19. @ContextConfiguration(locations = { "classpath:applicationContext-validator.xml" })

  20. public class CaseCadeTest {

  21. @Autowired

  22. private Validator validator;

  23. @Test

  24. public void test(){

  25. Order order = new Order();

  26. OrderItem item = new OrderItem();

  27. order.setOrderName("chuanhongliu");

  28. item.setEmail("12345");

  29. item.setItemCode("123456");

  30. item.setItemName("love");

  31. order.setOrderItem(item);

  32. try {

  33. validator.validate(order);

  34. } catch (ValidException e) {

  35. System.out.println(e.getAllMessage());

  36. }

  37. }

  38. }

5.3. 校驗組分組校驗

校驗組能夠在驗證的時候選擇應用哪些約束條件。校驗組是否通過可變引數傳遞給validate()、validateProperty()、validateValue()方法的,校驗的過程中,只有被指定的校驗組起作用。

設定校驗組共分為三個步驟:

1、建立校驗組介面:

  1. public interface AvailableFlightGroup {

  2. }

  3. public interface DestinationFlightGroup {

  4. }

2、指定校驗組:

  1. public class SegmentVo {

  2. @NotNull

  3. @NotEmpty(groups={AvailableFlightGroup.class})

  4. private String deptCity;

  5. @Pattern(regexp="[A-Z]{3}", groups={DestinationFlightGroup.class})

  6. @NotEmpty(groups={DestinationFlightGroup.class})

  7. private String arrCity;

  8. @DateValidator(regexp="yyyyMMdd", message="日期格式不正確,正確的格式類似於19980413")

  9. @NotEmpty

  10. private String deptDate;

  11. @NotEmpty

  12. private String arrDate;

  13. @NotEmpty

  14. @Email

  15. private String email;

  16. }

3、使用校驗組:

  1. @RunWith(SpringJUnit4ClassRunner.class)

  2. @ContextConfiguration(locations = { "classpath:applicationContext-validator.xml" })

  3. public class GroupTest {

  4. @Autowired

  5. private Validator validator;

  6. @Test

  7. public void test(){

  8. SegmentVo vo = new SegmentVo();

  9. vo.setDeptCity("PEK");

  10. vo.setArrCity("SH");

  11. vo.setDeptDate("20141026");

  12. vo.setArrDate("");

  13. try {

  14. validator.validate(vo,DestinationFlightGroup.class);

  15. validator.validate(vo,AvailableFlightGroup.class);

  16. } catch (ValidException e) {

  17. System.out.println(e.getMessage());

  18. System.out.println(e.getAllMessage());

  19. }

  20. }

  21. @Test

  22. public void test1(){

  23. SegmentVo vo = new SegmentVo();

  24. vo.setDeptCity("PEK");

  25. vo.setArrCity("SH");

  26. vo.setDeptDate("20141026");

  27. vo.setArrDate("");

  28. try {

  29. validator.validate(vo, DestinationFlightGroup.class, AvailableFlightGroup.class);

  30. } catch (ValidException e) {

  31. System.out.println(e.getMessage());

  32. System.out.println(e.getAllMessage());

  33. }

  34. }

  35. }

注意:如果指定了校驗組,則該屬性將不再屬於預設的校驗組Default.class,則在省略校驗組引數的情況下,將不會校驗自定義校驗組的屬性。推薦大家重新自定義校驗組。可以參考自定義預設校驗組的功能。

再注意:其實校驗組的介面可以不用專門定義,針對特定業務介面的校驗分組,可以直接指定特定的業務介面即可,這樣校驗分組就同業務介面做到了完整的統一。

5.4. 校驗組序列

可以按照指定的順序來逐個校驗各個校驗組。如果這個校驗組中有一個約束條件沒有通過的話,那麼此約束條件後面的都不會再繼續被校驗。

1、定義校驗組序列

  1. @GroupSequence({Default.class, AvailableFlightGroup.class, DestinationFlightGroup.class})

  2. public interface OrderChecks {

  3. }

2、使用校驗組序列

  1. @RunWith(SpringJUnit4ClassRunner.class)

  2. @ContextConfiguration(locations = { "classpath:applicationContext-validator.xml" })

  3. public class OrderCheckTest {

  4. @Autowired

  5. private Validator validator;

  6. @Test

  7. public void test(){

  8. Flight flight = new Flight();

  9. flight.setDeptCity("PE");

  10. flight.setArrCity("");

  11. try {

  12. validator.validate(flight, OrderChecks.class);

  13. } catch (ValidException e) {

  14. System.out.println(e.getMessage());

  15. }

  16. }

  17. }

5.5. 自定義預設校驗組功能

由於一個物件的屬性指定了一個校驗組後,將不再屬於預設校驗組。如此一來,如果想要校驗全部的屬性值,那麼將要注入預設校驗組和所有的自定義校驗組,使用起來非常的不方便。所以這裡提供了自定義預設校驗組的功能。

注意:自定義預設校驗組的時候,需要將類加入到@GroupSequence註解中。見紅色標註

  1. @GroupSequence({DefaultGroup.class,AvailableFlightGroup.class, DestinationFlightGroup.class})

  2. public class DefaultGroup {

  3. @NotEmpty(groups={DestinationFlightGroup.class})

  4. private String name;

  5. @Email(groups={AvailableFlightGroup.class})

  6. private String email;

  7. @AssertTrue

  8. private boolean man;

  9. }

同校驗組序列相比,僅僅是將@GroupSequence註解直接打在了類上面。

注意:因為不能在校驗組和校驗序列中有迴圈依賴的關係,所以,如果你想重定義一個類的預設組,並且還想把Default.class組加入到這個重定義的序列當中的話,則不能簡單的加入Default.class,而是需要把被重定義的類加入到其中。

5.6. 自定義智慧預設校驗組功能

Hibernate Validator提供了非標準的@GroupSequenceProvider註解。本功能提供根據當前物件例項的狀態,來決定載入那些校驗組進入預設校驗組。

1.定義GroupSequenceProvider:

  1. public class BMWGroupSequenceProvider implements DefaultGroupSequenceProvider<BMW> {

  2. @Override

  3. public List<Class<?>> getValidationGroups(BMW car) {

  4. List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();

  5. defaultGroupSequence.add(BMW.class);

  6. // defaultGroupSequence.add(Group2.class);

  7. if (car != null) {

  8. defaultGroupSequence.add(Group2.class);

  9. }

  10. return defaultGroupSequence;

  11. }

  12. }

2.註解自定義的GroupSequenceProvider:

  1. @GroupSequenceProvider(BMWGroupSequenceProvider.class)

  2. public class BMW extends Car{

  3. @NotNull

  4. @Valid

  5. private Driver driver;

  6. @Min(0)

  7. private int passager;

  8. @AssertFalse(groups = BMW.class)

  9. private boolean flag;

  10. }

其中必須實現DefaultGroupSequenceProvider介面,同時可以根據被注入的物件的狀態或者當前屬性值來決定載入哪些校驗組。

5.7. 自定義校驗註解

先學習定義一個簡單的註解,而後定義一個支援多個屬性值和校驗組功能的註解。

5.7.1. 定義一個簡單的註解

開發人員可以自定義校驗註解,一方面可以實現邏輯比較複雜的校驗,另一方面也可以整合多個校驗註解。下面給出了一個自定義的校驗字串大小寫的校驗規則,我們命名為CheckCase。

1、定義列舉型別

  1. public enum CaseMode {

  2. UPPER,

  3. LOWER;

  4. }

2、定義一個CheckCase的約束註解

  1. @Target({ METHOD, FIELD, ANNOTATION_TYPE })

  2. @Retention(RUNTIME)

  3. @Constraint(validatedBy = CheckCaseValidator.class)

  4. @Documented

  5. public @interface CheckCase {

  6. String message() default "自定義標註校驗失敗";

  7. Class<?>[] groups() default {};

  8. Class<? extends Payload>[] payload() default {};

  9. CaseMode value();

  10. }

3、定義一個CheckCase的約束的驗證器

  1. public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {

  2. private CaseMode caseMode;

  3. public void initialize(CheckCase constraintAnnotation) {

  4. this.caseMode = constraintAnnotation.value();

  5. }

  6. public boolean isValid(String object, ConstraintValidatorContext constraintContext) {

  7. boolean isUpper = false;

  8. if (object == null)

  9. return true;

  10. boolean isValid;

  11. if (caseMode == CaseMode.UPPER) {

  12. isValid = object.equals(object.toUpperCase());

  13. isUpper = true;

  14. }

  15. else {

  16. isValid = object.equals(object.toLowerCase());

  17. }

  18. if(!isValid) {

  19. constraintContext.disableDefaultConstraintViolation();

  20. if (isUpper){

  21. constraintContext.buildConstraintViolationWithTemplate("字串必須為全大寫").addConstraintViolation();

  22. }else{

  23. constraintContext.buildConstraintViolationWithTemplate("字串必須為全小寫").addConstraintViolation();

  24. }

  25. }

  26. return isValid;

  27. }

  28. }

4、使用自定義的CheckCase註解

  1. public class ConstraintbyselfModel {

  2. @CheckCase(value=CaseMode.UPPER)

  3. private String name;

  4. /**

  5. * @return the name

  6. */

  7. public String getName() {

  8. return name;

  9. }

  10. /**

  11. * @param name the name to set

  12. */

  13. public void setName(String name) {

  14. this.name = name;

  15. }

  16. }

自定義Check Constraint的實質就是獲取註解的物件, 並對物件做一些邏輯上的解析,判斷是否符合校驗邏輯,並返回自定義的校驗資訊。

5.7.2. 定義一個高階的註解

上面定義的CheckCase的標籤是一個比較簡單的標籤,下面定義一個可以設定多個屬性,並且支援分組功能的標籤。定義一個檢測List<String>中各個String是否滿足指定正則表示式的標籤ListStringPattern。

1、定義一個約束註解ListStringPattern

  1. @Target({ METHOD, FIELD, ANNOTATION_TYPE })

  2. @Retention(RUNTIME)

  3. @Constraint(validatedBy = ListStringPatternValidator.class)

  4. @Documented

  5. public @interface ListStringPattern {

  6. String message() default "List<String>中的String不符合指定的正則表示式";

  7. Class<?>[] groups() default {};

  8. Class<? extends Payload>[] payload() default {};

  9. String regexp();

  10. /**

  11. * Defines several {@link ListStringPattern} annotations on the same element.

  12. *

  13. * @see ListStringPattern

  14. */

  15. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })

  16. @Retention(RUNTIME)

  17. @Documented

  18. @interface List {

  19. ListStringPattern[] value();

  20. }

  21. }

其中裡面所定義的屬性值,如groups、regex等等,都是可以在標籤中直接指定的,而@interface List內建標籤的作用是可以通知支援多個Annotation修飾同一個欄位,例如:

@ListStringPattern.List({@ListStringPattern(regexp="^liu"),@ListStringPattern(regexp="hong$")})

2、定義約束ListStringPattern的約束校驗器

  1. public class ListStringPatternValidator implements ConstraintValidator<ListStringPattern, List<String>> {

  2. private String pattern;

  3. public void initialize(ListStringPattern constraintAnnotation) {

  4. this.pattern = constraintAnnotation.regexp();

  5. }

  6. public boolean isValid(List<String> list, ConstraintValidatorContext constraintContext) {

  7. if (list == null) {

  8. return true;

  9. }

  10. String errorString = null;

  11. boolean isValid = true;

  12. if (pattern == null || "".equals(pattern.trim())) {

  13. isValid = false;

  14. if (!isValid) {

  15. constraintContext.disableDefaultConstraintViolation();

  16. constraintContext.buildConstraintViolationWithTemplate("指定的正則表示式為空").addConstraintViolation();

  17. }

  18. return isValid;

  19. } else {

  20. Iterator<String> iter = list.iterator();

  21. while(iter.hasNext()){

  22. String string = iter.next();

  23. if(!string.matches(pattern)){

  24. isValid = false;

  25. errorString = string;

  26. break;

  27. }

  28. }

  29. }

  30. if (!isValid) {

  31. constraintContext.disableDefaultConstraintViolation();

  32. constraintContext.buildConstraintViolationWithTemplate("List<String>為空,或者其中的"+errorString+"不符合正則表示式:"+pattern).addConstraintViolation();

  33. }

  34. return isValid;

  35. }

  36. }

3、ListStringPattern約束的使用方法

  1. @ListStringPattern(regex="[a-zA-Z]{1}")

  2. private List<String> list;

5.8. 類校驗——類屬性的關聯校驗

類級別的約束,本質上就是自定義校驗的約束註解。當一個約束被註解在類上的時候,這個類的例項物件被傳遞給ConstraintValidator。當需要同時校驗多個屬性或者一個屬性的校驗依賴於其他屬性虛資訊的時候,類級別的校驗十分有用。下面提供一個校驗乘客數不能大約座位數的校驗用例,方法和過程同自定義約束註解。

1、定義一個檢驗旅客數目合法性的註解

  1. @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Constraint(validatedBy={ValidPersonNumberValidator.class})

  4. @Documented

  5. public @interface ValidPersonNumber {

  6. String message()default "Person中的人數不合法!";

  7. Class<?>[] groups()default{};

  8. Class<? extends Payload>[] payload()default{};

  9. }

2、定義一個驗證器

  1. public class ValidPersonNumberValidator implements ConstraintValidator<ValidPersonNumber, Person>{

  2. @Override

  3. public void initialize(ValidPersonNumber constraintAnnotation) {

  4. }

  5. @Override

  6. public boolean isValid(Person value, ConstraintValidatorContext context) {

  7. if(null == value){

  8. return true;

  9. }

  10. boolean isValid = value.getNumber()<=100;

  11. if(!isValid){

  12. context.disableDefaultConstraintViolation();

  13. context.buildConstraintViolationWithTemplate("Person類的總人不不能大於100").addConstraintViolation();

  14. }

  15. return isValid;

  16. }

  17. }

3、使用方法

  1. @ValidPersonNumber

  2. public class Person {

  3. private int number;

  4. /**

  5. * @return the number

  6. */

  7. public int getNumber() {

  8. return number;

  9. }

  10. /**

  11. * @param number the number to set

  12. */

  13. public void setNumber(int number) {

  14. this.number = number;

  15. }

  16. }

4、測試類

  1. @ValidPersonNumber

  2. @RunWith(SpringJUnit4ClassRunner.class)

  3. @ContextConfiguration(locations = { "classpath:applicationContext-validator.xml" })

  4. public class PersonTest {

  5. @Autowired

  6. private Validator validator;

  7. @Test

  8. public void test(){

  9. Person person = new Person();

  10. person.setNumber(110);

  11. try {

  12. validator.validate(person);

  13. } catch (ValidException e) {

  14. System.out.println(e.getMessage());

  15. }

  16. }

  17. }

5.9. 約束條件組合校驗

在某些複雜的場景中, 可能還會有更多的約束條件被定義到同一個元素上面, 這可能會讓程式碼看起來有些複雜, 另外, 如果在另外的類裡面還有一個相同的屬性, 我們可能還要把這些約束條件再拷貝到這個屬性上, 但是這樣做又違反了 DRY 原則.這個問題可以通過使用組合約束條件來解決.

1、定義一個約束條件組合的註解

  1. @NotNull

  2. @NotBlank

  3. @Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })

  4. @Retention(RetentionPolicy.RUNTIME)

  5. @Constraint(validatedBy = {})

  6. @Documented

  7. public @interface NotNullAndValidCheck {

  8. String message() default "必須不為null且屬性格式符合要求";

  9. Class<?>[] groups() default {};

  10. Class<? extends Payload>[] payload() default {};

  11. }

2、約束條件組合註解的使用

  1. public class Order1 {

  2. @NotEmpty

  3. private String orderName;

  4. @NotNullAndValidCheck

  5. private OrderItem orderItem;

3、測試類

  1. @RunWith(SpringJUnit4ClassRunner.class)

  2. @ContextConfiguration(locations = { "classpath:applicationContext-validator.xml" })

  3. public class NotNullAndValidCheckTest {

  4. @Autowired

  5. private Validator validator;

  6. @Test

  7. public void test(){

  8. Order1 order = new Order1();

  9. order.setOrderName("chuanhongliu");

  10. try {

  11. validator.validate(order);

  12. } catch (ValidException e) {

  13. System.out.println(e.getMessage());

  14. }

  15. }

  16. }

6. 各個功能的使用場景

本章主要供大家認識各個校驗功能的適用場景,大家根據這些適用場景並結合自己的具體需求,來整合多種校驗功能,確定自己的開發策略。

6.1. 內建註解校驗

本框架主要是通過打註解(註解)的方式去校驗Bean中的屬性,現成可用的註解有Bean Validator內建的註解和Hibernate Validator拓展的註解以及Validator自帶的註解三種,可以直接使用。父類的校驗配置會自動被子類繼承。

適用場景:簡單的單屬性校驗。

6.2. 物件圖級聯校驗

對於一個物件中的成員物件屬性,打上@Valid註解後,會自動實現對成員物件的級聯校驗。

本功能適用於複雜的物件,其成員屬性為一個物件。

6.3. 校驗組分組校驗

可以對不同的屬性進行分組,以便在特定場景下,僅僅校驗一個或幾個校驗組。一個屬性可以歸屬為多個校驗組。

適用場景:將需要校驗的屬性分類,並單獨按組校驗。

6.4. 校驗組序列

對於已經分成多個校驗組的物件,可以配置校驗組序列,讓各個校驗組按照一定的順序來進行校驗。

適用場景:校驗組的順序校驗。

6.5. 自定義預設校驗組

一個物件會有一個預設的校驗組,包含了所有的未分組的校驗項。校驗預設組的時候不需要在校驗方法中指明校驗組。如果想讓預設校驗組包含所有的分組的校驗項,可以使用自定義的預設校驗組。這樣如果想校驗整個的物件,就不需要再方法中指定校驗組了。大大提高了使用的便利性。推薦所有劃分校驗組的物件都重新自定義預設校驗組。

適用場景:有校驗組的情況下,重新定義預設校驗組,以包含所有的校驗項。

6.6. 自定義智慧預設校驗組

對於自定義的預設校驗組,會有很多的校驗組組成。如果想根據當前物件的不同狀態來確定預設校驗組的組成,決定到底去校驗哪些校驗組,可以自定義智慧預設校驗組。

適用場景:根據物件的不同狀態,來決定校驗哪些校驗組。

6.7. 自定義校驗註解

對於某一個屬性有多重校驗約束,或者一些邏輯上的校驗約束,可以自定義校驗註解,以便整合多重校驗約束,或者提供複雜邏輯的校驗約束。

適用場景:針對某一個屬性值的複雜的校驗邏輯,需要自定義。

6.8. 類校驗-類屬性關聯校驗

對類校驗,註解打在類名上。並可以判斷物件中多個屬性值之間是否滿足某種業務邏輯,來判斷是否通過校驗。

適用場景:物件多個屬性值的關聯校驗。

7. 附件

7.1. 依賴的pom檔案

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  3. <modelVersion>4.0.0</modelVersion>

  4. <groupId>com.chhliu.common</groupId>

  5. <artifactId>validate</artifactId>

  6. <version>2.0.0</version>

  7. <packaging>jar</packaging>

  8. <name>validate</name>

  9. <url>http://maven.apache.org</url>

  10. <properties>

  11. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  12. <spring-version>3.2.5.RELEASE</spring-version>

  13. <junit.version>4.10</junit.version>

  14. </properties>

  15. <dependencies>

  16. <dependency>

  17. <groupId>org.springframework</groupId>

  18. <artifactId>spring-test</artifactId>

  19. <version>${spring-version}</version>

  20. <scope>test</scope>

  21. </dependency>

  22. <dependency>

  23. <groupId>org.springframework</groupId>

  24. <artifactId>spring-beans</artifactId>

  25. <version>${spring-version}</version>

  26. </dependency>

  27. <dependency>

  28. <groupId>org.springframework</groupId>

  29. <artifactId>spring-core</artifactId>

  30. <version>${spring-version}</version>

  31. </dependency>

  32. <dependency>

  33. <groupId>org.springframework</groupId>

  34. <artifactId>spring-context</artifactId>

  35. <version>${spring-version}</version>

  36. </dependency>

  37. <dependency>

  38. <groupId>junit</groupId>

  39. <artifactId>junit</artifactId>

  40. <version>${junit.version}</version>

  41. <scope>test</scope>

  42. </dependency>

  43. <dependency>

  44. <groupId>commons-logging</groupId>

  45. <artifactId>commons-logging</artifactId>

  46. <version>1.1.1</version>

  47. </dependency>

  48. <dependency>

  49. <groupId>org.apache.commons</groupId>

  50. <artifactId>commons-lang3</artifactId>

  51. <version>3.1</version>

  52. </dependency>

  53. <dependency>

  54. <groupId>org.hibernate</groupId>

  55. <artifactId>hibernate-validator</artifactId>

  56. <version>5.1.0.Final</version>

  57. </dependency>

  58. <dependency>

  59. <groupId>javax.el</groupId>

  60. <artifactId>el-api</artifactId>

  61. <version>2.2.1-b04</version>

  62. </dependency>

  63. <dependency>

  64. <groupId>com.sun.el</groupId>

  65. <artifactId>el-ri</artifactId>

  66. <version>1.0</version>

  67. </dependency>

  68. <dependency>

  69. <groupId>javax.validation</groupId>

  70. <artifactId>validation-api</artifactId>

  71. <version>1.1.0.Final</version>

  72. </dependency>

  73. <dependency>

  74. <groupId>com.fasterxml</groupId>

  75. <artifactId>classmate</artifactId>

  76. <version>0.5.4</version>

  77. </dependency>

  78. </dependencies>

  79. </project>

注意:具體的jar包視工程型別而定,例如,如果你只是一個普通的java專案,則需要加入el包,如果是在容器中執行的專案,例如JBOSS AS,則不需要引入此包,因為該容器已經提供了這個jar包

7.2. Validator校驗錯誤碼

錯誤碼

錯誤資訊

20141031

validate功能校驗物件不可為空!

20141032

校驗的物件沒有該屬性

20141033

校驗失敗!

20141034

校驗的物件不合法!

20141035

校驗的屬性值不合法!

20141036

校驗的輸入值不合法!

說明:開發人員可以根據返回的錯誤碼,進行進一步的處理。