# 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)中找到。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.shiker.tech/spring-he-xin-gong-neng/3.-yan-zheng-shu-ju-bang-ding-he-lei-xing-zhuan-huan/3.1.-shi-yong-spring-de-validator-jie-kou-jin-xing-yan-zheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
