# 3.1. 使用 Spring 的 Validator 接口进行验证

Spring 提供了一个`Validator`接口，您可以使用它来验证对象。 `Validator`接口通过使用一个`Errors`对象来工作，以便在验证时，验证器可以向`Errors`对象报告验证失败。

考虑以下小的数据对象的示例：

```java
public class Person {

    private String name;
    private int age;

    // the usual getters and setters...
}
```

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

* `supports(Class)`：这个`Validator`可以验证提供的实例`Class`吗？
* `validate(Object, org.springframework.validation.Errors)`：验证给定对象，并在验证错误的情况下，将这些对象注册到给定`Errors`对象。

实现`Validator`相当简单，尤其是当您知道 Spring 框架还提供了`ValidationUtils`帮助程序类时。以下示例为`Person`实例实现`Validator`：

```java
public class PersonValidator implements Validator {

    /**
     * This Validator validates only Person instances
     */
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
        Person p = (Person) obj;
        if (p.getAge() < 0) {
            e.rejectValue("age", "negativevalue");
        } else if (p.getAge() > 110) {
            e.rejectValue("age", "too.darn.old");
        }
    }
}
```

`ValidationUtils`类上的`static` `rejectIfEmpty(..)`方法用于拒绝接受`name`属性（如果是）`null`或空字符串。查看 [`ValidationUtils`](https://docs.spring.io/spring-framework/docs/5.3.22/javadoc-api/org/springframework/validation/ValidationUtils.html)javadoc 以了解除了前面显示的示例之外它还提供了哪些功能。

虽然实现单个`Validator`类来验证丰富对象中的每个嵌套对象当然是可能的，但最好将每个嵌套对象类的验证逻辑封装在其自己的`Validator`实现中。“丰富”对象的一个`Customer`简单示例是由两个`String` 属性（第一个和第二个名称）和一个复杂`Address`对象组成的。`Address`对象可以独立于`Customer`对象使用，因此实现了不同的 `AddressValidator` 。如果您希望 `CustomerValidator` 重用 `AddressValidator` 类中包含的逻辑而不诉诸复制和粘贴，您可以在 `CustomerValidator` 中依赖注入或实例化一个 `AddressValidator`，如以下示例所示：

```java
public class CustomerValidator implements Validator {

    private final Validator addressValidator;

    public CustomerValidator(Validator addressValidator) {
        if (addressValidator == null) {
            throw new IllegalArgumentException("The supplied [Validator] is " +
                "required and must not be null.");
        }
        if (!addressValidator.supports(Address.class)) {
            throw new IllegalArgumentException("The supplied [Validator] must " +
                "support the validation of [Address] instances.");
        }
        this.addressValidator = addressValidator;
    }

    /**
     * This Validator validates Customer instances, and any subclasses of Customer too
     */
    public boolean supports(Class clazz) {
        return Customer.class.isAssignableFrom(clazz);
    }

    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
        Customer customer = (Customer) target;
        try {
            errors.pushNestedPath("address");
            ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
        } finally {
            errors.popNestedPath();
        }
    }
}
```

验证错误会报告传递给验证器的`Errors`对象。在 Spring Web MVC 的情况下，您可以使用`<spring:bind/>`标签来检查错误消息，但您也可以自己检查`Errors`对象。关于它提供的方法的更多信息可以在[javadoc](https://docs.spring.io/spring-framework/docs/5.3.22/javadoc-api/org/springframework/validation/Errors.html)中找到。
