Spring 有一个 Validator 接口,你可以用它来验证对象。验证器接口通过使用一个 Errors 对象来工作,这样,在验证时,验证器可以向 Errors 对象报告验证失败。

    请考虑下面这个小数据对象的例子:

    1. public class Person {
    2. private String name;
    3. private int age;
    4. // 省略了 getters 和 setters 方法
    5. }

    下一个例子通过实现 org.springframework.validation.Validator接口的以下两个方法为 Person 类提供验证行为:

    • supports(Class):这个验证器可以验证提供的类的实例吗?
    • validate(Object, org.springframework.validation.Errors):验证给定的对象,如果出现验证错误,则用给定的 Errors 对象登记这些错误。.

    实现一个验证器是相当简单的,特别是当你知道 Spring 框架也提供的 ValidationUtils 帮助类时。下面的例子为 Person 实例实现了 Validator。

    1. package cn.mrcode.study.springdocsread.data;
    2. import org.springframework.validation.Errors;
    3. import org.springframework.validation.ValidationUtils;
    4. import org.springframework.validation.Validator;
    5. /**
    6. * @author mrcode
    7. * @date 2022/3/1 13:26
    8. */
    9. public class PersonValidator implements Validator {
    10. /**
    11. * 这个验证器只验证 Person 的实例
    12. *
    13. * @param clazz
    14. * @return
    15. */
    16. @Override
    17. public boolean supports(Class<?> clazz) {
    18. return Person.class.equals(clazz);
    19. }
    20. @Override
    21. public void validate(Object obj, Errors e) {
    22. // 如果对象中的 name 字段为 空字符串或则 null ,则将给定的错误信息(errorCode)存放在 errors 中
    23. ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
    24. Person p = (Person) obj;
    25. if (p.getAge() < 0) {
    26. // 添加错误信息
    27. e.rejectValue("age", "negativevalue");
    28. } else if (p.getAge() > 110) {
    29. e.rejectValue("age", "too.darn.old");
    30. }
    31. }
    32. }

    ValidationUtils 类的静态方法 rejectIfEmpty(..)用于拒绝 name 属性,如果它是 null 或空字符串。看一下 ValidationUtils 的 javadoc,看看除了前面显示的例子外,它还提供了哪些功能。

    下面是使用方式

    1. package cn.mrcode.study.springdocsread.data;
    2. import org.springframework.validation.FieldError;
    3. import org.springframework.validation.MapBindingResult;
    4. import java.util.HashMap;
    5. import java.util.List;
    6. /**
    7. * @author mrcode
    8. * @date 2022/3/1 13:38
    9. */
    10. public class DemoTest {
    11. public static void main(String[] args) {
    12. final PersonValidator validator = new PersonValidator();
    13. final MapBindingResult errors = new MapBindingResult(new HashMap<>(), "persion");
    14. validator.validate(new Person(), errors);
    15. final List<FieldError> fieldErrors = errors.getFieldErrors();
    16. for (FieldError fieldError : fieldErrors) {
    17. System.out.println("字段=" + fieldError.getField() + " ;错误码=" + fieldError.getCode());
    18. System.out.println(fieldError);
    19. }
    20. }
    21. }

    输出信息如下

    1. 字段=name ;错误码=name.empty
    2. // 这个里面的 codes 不仅仅是 name.empty,还有其他的,这个下一个章节会讲为什么会多出来两个 code
    3. Field error in object 'persion' on field 'name': rejected value [null]; codes [name.empty.persion.name,name.empty.name,name.empty]; arguments []; default message [null]

    当然,实现一个单一的 Validator 类来验证富对象中的每个嵌套对象是可能的,但最好是将每个嵌套类对象的验证逻辑封装在自己的 Validator 实现中。一个简单的 「丰富」对象的例子是一个 Customer ,它由两个字符串属性(一个名字和一个第二名字)和一个复杂的 Address 对象组成。Address 对象可以独立于客户对象使用,所以已经实现了一个独立的 AddressValidator。如果你想让你的 CustomerValidator 重用 AddressValidator 类中所包含的逻辑,而不采用复制和粘贴的方式,你可以在 CustomerValidator 中依赖注入或实例化一个 AddressValidator,如下面的例子所示:

    1. public class CustomerValidator implements Validator {
    2. private final Validator addressValidator;
    3. public CustomerValidator(Validator addressValidator) {
    4. if (addressValidator == null) {
    5. throw new IllegalArgumentException("The supplied [Validator] is " +
    6. "required and must not be null.");
    7. }
    8. // 如果传入的验证器不支持 Address 地址
    9. if (!addressValidator.supports(Address.class)) {
    10. throw new IllegalArgumentException("The supplied [Validator] must " +
    11. "support the validation of [Address] instances.");
    12. }
    13. this.addressValidator = addressValidator;
    14. }
    15. /**
    16. * 这个验证器可以验证客户实例,以及客户的任何子类。
    17. */
    18. public boolean supports(Class clazz) {
    19. return Customer.class.isAssignableFrom(clazz);
    20. }
    21. public void validate(Object target, Errors errors) {
    22. ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
    23. ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
    24. Customer customer = (Customer) target;
    25. try {
    26. // 验证下一个嵌套对象
    27. errors.pushNestedPath("address");
    28. // 这里使用传入的验证器进行验证
    29. ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
    30. } finally {
    31. errors.popNestedPath();
    32. }
    33. }
    34. }

    验证错误被报告给传递给验证器的 Errors 对象。在 Spring Web MVC 中,你可以使用 <spring:bind/>标签来检查错误信息,但你也可以自己检查 Errors 对象。关于它提供的方法的更多信息可以在 javadoc 中找到。