(#validation)3. Validation, Data Binding, and Type Conversion

预计阅读时间: 71 分钟

3. 验证、数据绑定和类型转换

There are pros and cons for considering validation as business logic, and Spring offers a design for validation (and data binding) that does not exclude either one of them.
考虑将验证视为业务逻辑有其利弊,Spring 提供了一种设计(用于验证和数据绑定),它不排除其中的任何一个。
Specifically, validation should not be tied to the web tier and should be easy to localize, and it should be possible to plug in any available validator. Considering these concerns, Spring provides a Validator contract that is both basic and eminently usable in every layer of an application.
具体来说,验证不应与 Web 层绑定,并且应该易于本地化,同时应该能够插入任何可用的验证器。考虑到这些担忧,Spring 提供了一个既基本又极其适用于应用程序每一层的 Validator 契约。

Data binding is useful for letting user input be dynamically bound to the domain model of an application (or whatever objects you use to process user input). Spring provides the aptly named DataBinder to do exactly that. The Validator and the DataBinder make up the validation package, which is primarily used in but not limited to the web layer.
数据绑定对于将用户输入动态绑定到应用程序的领域模型(或用于处理用户输入的任何对象)非常有用。Spring 提供了一个恰如其分的名为 DataBinder 的注解来完成这项任务。 ValidatorDataBinder 共同构成了 validation 包,该包主要用于但不限于 Web 层。

The BeanWrapper is a fundamental concept in the Spring Framework and is used in a lot of places. However, you probably do not need to use the BeanWrapper directly. Because this is reference documentation, however, we felt that some explanation might be in order. We explain the BeanWrapper in this chapter, since, if you are going to use it at all, you are most likely do so when trying to bind data to objects.
BeanWrapper 是 Spring 框架中的一个基本概念,在许多地方都有使用。然而,你可能不需要直接使用 BeanWrapper 。因为这是参考文档,所以我们觉得有必要进行一些解释。我们将在本章中解释 BeanWrapper ,因为如果你要使用它,你很可能会在尝试将数据绑定到对象时使用它。

Spring’s DataBinder and the lower-level BeanWrapper both use PropertyEditorSupport implementations to parse and format property values. The PropertyEditor and PropertyEditorSupport types are part of the JavaBeans specification and are also explained in this chapter. Spring 3 introduced a core.convert package that provides a general type conversion facility, as well as a higher-level “format” package for formatting UI field values. You can use these packages as simpler alternatives to PropertyEditorSupport implementations. They are also discussed in this chapter.
Spring 的 DataBinder 和底层 BeanWrapper 都使用 PropertyEditorSupport 实现来解析和格式化属性值。 PropertyEditorPropertyEditorSupport 类型是 JavaBeans 规范的一部分,也在本章中进行了解释。Spring 3 引入了一个 core.convert 包,该包提供了一般类型转换功能,以及一个用于格式化 UI 字段值的高级“format”包。您可以使用这些包作为 PropertyEditorSupport 实现的更简单替代方案。它们也在本章中进行了讨论。

Spring supports Java Bean Validation through setup infrastructure and an adaptor to Spring’s own Validator contract. Applications can enable Bean Validation once globally, as described in Java Bean Validation, and use it exclusively for all validation needs. In the web layer, applications can further register controller-local Spring Validator instances per DataBinder, as described in Configuring a DataBinder, which can be useful for plugging in custom validation logic.
Spring 通过设置基础设施和适配器支持 Java Bean Validation,并符合 Spring 自己的 Validator 契约。应用程序可以在全局范围内启用 Bean Validation,如 Java Bean Validation 中所述,并专门用于所有验证需求。在 Web 层,应用程序可以进一步在每个 DataBinder 中注册控制器本地的 Spring Validator 实例,如配置 DataBinder 中所述,这有助于插入自定义验证逻辑。

(#validator)3.1. Validation by Using Spring’s Validator Interface

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

Spring features a Validator interface that you can use to validate objects. The Validator interface works by using an Errors object so that, while validating, validators can report validation failures to the Errors object.
Spring 提供了一个 Validator 接口,您可以使用它来验证对象。 Validator 接口通过使用 Errors 对象来工作,这样在验证时,验证器可以将验证失败报告给 Errors 对象。

Consider the following example of a small data object:
考虑以下小型数据对象的示例:

public class Person { private String name; private int age; // the usual getters and setters... }
class Person(val name: String, val age: Int)

The next example provides validation behavior for the Person class by implementing the following two methods of the org.springframework.validation.Validator interface:
下一个示例通过实现接口的以下两个方法为 Person 类提供验证行为:

  • supports(Class): Can this Validator validate instances of the supplied Class?
    supports(Class) : 这个 Validator 能否验证提供的 Class 的实例?

  • validate(Object, org.springframework.validation.Errors): Validates the given object and, in case of validation errors, registers those with the given Errors object.
    validate(Object, org.springframework.validation.Errors) :验证给定的对象,并在出现验证错误的情况下,将这些错误记录到给定的 Errors 对象中。

Implementing a Validator is fairly straightforward, especially when you know of the ValidationUtils helper class that the Spring Framework also provides. The following example implements Validator for Person instances:
实现一个 Validator 相当简单,尤其是当你知道 Spring 框架也提供了一个 ValidationUtils 辅助类时。以下示例为 Validator 实现了 Person 实例:

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"); } } }
class PersonValidator : Validator { /** * This Validator validates only Person instances */ override fun supports(clazz: Class<*>): Boolean { return Person::class.java == clazz } override fun validate(obj: Any, e: Errors) { ValidationUtils.rejectIfEmpty(e, "name", "name.empty") val p = obj as Person if (p.age < 0) { e.rejectValue("age", "negativevalue") } else if (p.age > 110) { e.rejectValue("age", "too.darn.old") } } }

The static rejectIfEmpty(..) method on the ValidationUtils class is used to reject the name property if it is null or the empty string. Have a look at the ValidationUtils javadoc to see what functionality it provides besides the example shown previously.
该类中的 static rejectIfEmpty(..) 方法用于拒绝 name 属性,如果它是 null 或空字符串。查看 ValidationUtils javadoc 以了解它除了之前示例所示之外的功能。

While it is certainly possible to implement a single Validator class to validate each of the nested objects in a rich object, it may be better to encapsulate the validation logic for each nested class of object in its own Validator implementation. A simple example of a “rich” object would be a Customer that is composed of two String properties (a first and a second name) and a complex Address object. Address objects may be used independently of Customer objects, so a distinct AddressValidator has been implemented. If you want your CustomerValidator to reuse the logic contained within the AddressValidator class without resorting to copy-and-paste, you can dependency-inject or instantiate an AddressValidator within your CustomerValidator, as the following example shows:
虽然确实可以实施一个单独的 Validator 类来验证丰富对象中的每个嵌套对象,但可能更好的是将每个嵌套对象的验证逻辑封装在其自己的 Validator 实现中。一个简单的“丰富”对象的例子是一个由两个 String 属性(一个名字和一个姓氏)和一个复杂的 Address 对象组成的 CustomerAddress 对象可以独立于 Customer 对象使用,因此已经实现了一个独特的 AddressValidator 。如果您想在您的 CustomerValidator 中重用 AddressValidator 类中包含的逻辑,而不必使用复制粘贴,您可以在您的 CustomerValidator 中依赖注入或实例化一个 AddressValidator ,如下例所示:

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(); } } }
class CustomerValidator(private val addressValidator: Validator) : Validator { init { if (addressValidator == null) { throw IllegalArgumentException("The supplied [Validator] is required and must not be null.") } if (!addressValidator.supports(Address::class.java)) { throw IllegalArgumentException("The supplied [Validator] must support the validation of [Address] instances.") } } /* * This Validator validates Customer instances, and any subclasses of Customer too */ override fun supports(clazz: Class<>): Boolean { return Customer::class.java.isAssignableFrom(clazz) } override fun validate(target: Any, errors: Errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required") ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required") val customer = target as Customer try { errors.pushNestedPath("address") ValidationUtils.invokeValidator(this.addressValidator, customer.address, errors) } finally { errors.popNestedPath() } } }

Validation errors are reported to the Errors object passed to the validator. In the case of Spring Web MVC, you can use the <spring:bind/> tag to inspect the error messages, but you can also inspect the Errors object yourself. More information about the methods it offers can be found in the javadoc.
验证错误报告给传递给验证器的 Errors 对象。在 Spring Web MVC 的情况下,您可以使用 <spring:bind/> 标签来检查错误消息,但您也可以自己检查 Errors 对象。有关它提供的方法的更多信息,请参阅 javadoc。

(#validation-conversion)3.2. Resolving Codes to Error Messages

3.2. 将代码解析为错误信息

We covered databinding and validation. This section covers outputting messages that correspond to validation errors. In the example shown in the preceding section, we rejected the name and age fields. If we want to output the error messages by using a MessageSource, we can do so using the error code we provide when rejecting the field (' name' and 'age' in this case). When you call (either directly, or indirectly, by using, for example, the ValidationUtils class) rejectValue or one of the other reject methods from the Errors interface, the underlying implementation not only registers the code you passed in but also registers a number of additional error codes. The MessageCodesResolver determines which error codes the Errors interface registers. By default, the DefaultMessageCodesResolver is used, which (for example) not only registers a message with the code you gave but also registers messages that include the field name you passed to the reject method. So, if you reject a field by using rejectValue("age", "too.darn.old"), apart from the too.darn.old code, Spring also registers too.darn.old.age and too.darn.old.age.int (the first includes the field name and the second includes the type of the field). This is done as a convenience to aid developers when targeting error messages.
我们涵盖了数据绑定和验证。本节介绍输出与验证错误相对应的消息。在上一节中展示的示例中,我们拒绝了 nameage 字段。如果我们想通过使用 MessageSource 来输出错误消息,我们可以使用在拒绝字段时提供的错误代码(在这种情况下是'name' 和'age')。当你调用(无论是直接调用还是通过例如使用 ValidationUtils 类间接调用) rejectValueErrors 接口的其他 reject 方法时,底层实现不仅注册了你传入的代码,还注册了多个额外的错误代码。 MessageCodesResolver 确定 Errors 接口注册了哪些错误代码。默认情况下,使用 DefaultMessageCodesResolver ,它(例如)不仅注册了你给出的代码的消息,还注册了包含你传递给拒绝方法的字段名称的消息。因此,如果你使用 rejectValue("age", "too.darn.old") 拒绝一个字段,除了 too.darn.old 代码外,Spring 还注册了 too.darn.old.agetoo.darn.old.age.int (第一个包含字段名称,第二个包含字段的类型)。这样做是为了方便开发者定位错误消息。

More information on the MessageCodesResolver and the default strategy can be found in the javadoc of MessageCodesResolver and DefaultMessageCodesResolver, respectively.
更多关于 MessageCodesResolver 和默认策略的信息可以在 MessageCodesResolverDefaultMessageCodesResolver 的 javadoc 中找到。

(#beans-beans)3.3. Bean Manipulation and theBeanWrapper

3.3. 面包屑操作和 BeanWrapper

The org.springframework.beans package adheres to the JavaBeans standard. A JavaBean is a class with a default no-argument constructor and that follows a naming convention where (for example) a property named bingoMadness would have a setter method setBingoMadness(..) and a getter method getBingoMadness(). For more information about JavaBeans and the specification, see javabeans.
org.springframework.beans 包遵循 JavaBeans 标准。JavaBean 是一个具有默认无参数构造函数且遵循命名约定的类,例如,名为 bingoMadness 的属性将具有 setBingoMadness(..) 的设置方法和一个 getBingoMadness() 的获取方法。有关 JavaBeans 和规范的更多信息,请参阅 javabeans。

One quite important class in the beans package is the BeanWrapper interface and its corresponding implementation ( BeanWrapperImpl). As quoted from the javadoc, the BeanWrapper offers functionality to set and get property values ( individually or in bulk), get property descriptors, and query properties to determine if they are readable or writable. Also, the BeanWrapper offers support for nested properties, enabling the setting of properties on sub-properties to an unlimited depth. The BeanWrapper also supports the ability to add standard JavaBeans PropertyChangeListeners and VetoableChangeListeners, without the need for supporting code in the target class. Last but not least, the BeanWrapper provides support for setting indexed properties. The BeanWrapper usually is not used by application code directly but is used by the DataBinder and the BeanFactory.
一个在 beans 包中非常重要的类是 BeanWrapper 接口及其对应实现( BeanWrapperImpl )。正如 javadoc 中所述, BeanWrapper 提供了设置和获取属性值(单个或批量)、获取属性描述符以及查询属性以确定它们是否可读或可写的功能。此外, BeanWrapper 还支持嵌套属性,允许对子属性的属性进行设置,深度不限。 BeanWrapper 还支持添加标准 JavaBeans PropertyChangeListenersVetoableChangeListeners ,无需在目标类中支持代码。最后但同样重要的是, BeanWrapper 提供了设置索引属性的支持。 BeanWrapper 通常不直接由应用程序代码使用,而是由 DataBinderBeanFactory 使用。

The way the BeanWrapper works is partly indicated by its name: it wraps a bean to perform actions on that bean, such as setting and retrieving properties.
这种方式的工作原理部分由其名称指示:它包装一个豆子以在该豆子上执行操作,例如设置和检索属性。

(#beans-beans-conventions)3.3.1. Setting and Getting Basic and Nested Properties

3.3.1. 设置和获取基本和嵌套属性

Setting and getting properties is done through the setPropertyValue and getPropertyValue overloaded method variants of BeanWrapper. See their Javadoc for details. The below table shows some examples of these conventions:
设置和获取属性是通过 BeanWrappersetPropertyValuegetPropertyValue 重载方法变体完成的。请参阅它们的 Javadoc 以获取详细信息。下表显示了这些约定的几个示例:

Table 11. Examples of properties
表 11. 属性示例

Expression 表达式

Explanation 说明

name

Indicates the property name that corresponds to the getName() or isName() and setName(..) methods.
指示与 getName()isName()setName(..) 方法相对应的属性 name

account.name

Indicates the nested property name of the property account that corresponds to (for example) the getAccount().setName() or getAccount().getName() methods.
指示属性 account 中嵌套属性 name 对应(例如) getAccount().setName()getAccount().getName() 方法。

account[2]

Indicates the third element of the indexed property account. Indexed properties can be of type array, list, or other naturally ordered collection.
指示索引属性 account 的第三个元素。索引属性可以是类型 arraylist 或其他有序集合。

account[COMPANYNAME]

Indicates the value of the map entry indexed by the COMPANYNAME key of the account Map property.
指示由 accountMap 属性的第 COMPANYNAME 键索引的映射条目的值。

(This next section is not vitally important to you if you do not plan to work with the BeanWrapper directly. If you use only the DataBinder and the BeanFactory and their default implementations, you should skip ahead to the section on PropertyEditors.)
(如果您不打算直接与 BeanWrapper 工作,那么下一部分对您来说并不至关重要。如果您只使用 DataBinderBeanFactory 及其默认实现,应跳过到 PropertyEditors 的部分。)

The following two example classes use the BeanWrapper to get and set properties:
以下两个示例类使用 BeanWrapper 来获取和设置属性:

public class Company { private String name; private Employee managingDirector; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Employee getManagingDirector() { return this.managingDirector; } public void setManagingDirector(Employee managingDirector) { this.managingDirector = managingDirector; } }
class Company { var name: String? = null var managingDirector: Employee? = null }
public class Employee { private String name; private float salary; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } }
class Employee { var name: String? = null var salary: Float? = null }

The following code snippets show some examples of how to retrieve and manipulate some of the properties of instantiated Companys and Employees:
以下代码片段展示了如何检索和操作实例化的 CompanyEmployee 的一些属性示例:

BeanWrapper company = new BeanWrapperImpl(new Company()); // setting the company name.. company.setPropertyValue("name", "Some Company Inc."); // ... can also be done like this: PropertyValue value = new PropertyValue("name", "Some Company Inc."); company.setPropertyValue(value); // ok, let's create the director and tie it to the company: BeanWrapper jim = new BeanWrapperImpl(new Employee()); jim.setPropertyValue("name", "Jim Stravinsky"); company.setPropertyValue("managingDirector", jim.getWrappedInstance()); // retrieving the salary of the managingDirector through the company Float salary = (Float) company.getPropertyValue("managingDirector.salary");
val company = BeanWrapperImpl(Company()) // setting the company name.. company.setPropertyValue("name", "Some Company Inc.") // ... can also be done like this: val value = PropertyValue("name", "Some Company Inc.") company.setPropertyValue(value) // ok, let's create the director and tie it to the company: val jim = BeanWrapperImpl(Employee()) jim.setPropertyValue("name", "Jim Stravinsky") company.setPropertyValue("managingDirector", jim.wrappedInstance) // retrieving the salary of the managingDirector through the company val salary = company.getPropertyValue("managingDirector.salary") as Float?

(#beans-beans-conversion)3.3.2. Built-inPropertyEditor Implementations

3.3.2. 内置 PropertyEditor 实现

Spring uses the concept of a PropertyEditor to effect the conversion between an Object and a String. It can be handy to represent properties in a different way than the object itself. For example, a Date can be represented in a human readable way (as the String: '2007-14-09'), while we can still convert the human readable form back to the original date (or, even better, convert any date entered in a human readable form back to Date objects). This behavior can be achieved by registering custom editors of type java.beans.PropertyEditor. Registering custom editors on a BeanWrapper or, alternatively, in a specific IoC container (as mentioned in the previous chapter), gives it the knowledge of how to convert properties to the desired type. For more about PropertyEditor, see the javadoc of the java.beans package from Oracle.
Spring 使用 PropertyEditor 的概念来实现 ObjectString 之间的转换。以不同于对象本身的方式表示属性可能很有用。例如, Date 可以以人类可读的方式表示(如 String : '2007-14-09' ),同时我们仍然可以将人类可读的形式转换回原始日期(或者更好的是,将任何以人类可读形式输入的日期转换回 Date 对象)。通过注册自定义编辑器类型 java.beans.PropertyEditor 可以实现这种行为。在 BeanWrapper 上注册自定义编辑器,或者,作为替代,在特定的 IoC 容器中(如前一章所述),它将获得如何将属性转换为所需类型的知识。有关 PropertyEditor 的更多信息,请参阅 Oracle 的 java.beans 包的 javadoc。

A couple of examples where property editing is used in Spring:
几个在 Spring 中使用属性编辑的示例:

  • Setting properties on beans is done by using PropertyEditor implementations. When you use String as the value of a property of some bean that you declare in an XML file, Spring (if the setter of the corresponding property has a Class parameter) uses ClassEditor to try to resolve the parameter to a Class object.
    设置 bean 的属性是通过使用 PropertyEditor 实现完成的。当你使用 String 作为你在 XML 文件中声明的某个 bean 的属性值时,如果相应属性的 setter 有 Class 参数,Spring 会使用 ClassEditor 尝试将参数解析为 Class 对象。

  • Parsing HTTP request parameters in Spring’s MVC framework is done by using all kinds of PropertyEditor implementations that you can manually bind in all subclasses of the CommandController.
    解析 Spring MVC 框架中的 HTTP 请求参数是通过使用各种 PropertyEditor 实现来完成的,您可以在 CommandController 的所有子类中手动绑定。

Spring has a number of built-in PropertyEditor implementations to make life easy. They are all located in the org.springframework.beans.propertyeditors package. Most, (but not all, as indicated in the following table) are, by default, registered by BeanWrapperImpl. Where the property editor is configurable in some fashion, you can still register your own variant to override the default one. The following table describes the various PropertyEditor implementations that Spring provides:
Spring 包含多个内置的 PropertyEditor 实现,使生活变得简单。它们都位于 org.springframework.beans.propertyeditors 包中。大多数(但并非所有,如以下表格所示)默认情况下由 BeanWrapperImpl 注册。在属性编辑器可配置的情况下,您仍然可以注册自己的变体以覆盖默认值。以下表格描述了 Spring 提供的各种 PropertyEditor 实现:

Table 12. Built-in PropertyEditor Implementations
表 12. 内置 PropertyEditor 实现

Class 班级

Explanation 说明

ByteArrayPropertyEditor

Editor for byte arrays. Converts strings to their corresponding byte representations. Registered by default by BeanWrapperImpl.
字节数组编辑器。将字符串转换为相应的字节表示。默认由 BeanWrapperImpl 注册。

ClassEditor

Parses Strings that represent classes to actual classes and vice-versa. When a class is not found, an IllegalArgumentException is thrown. By default, registered by BeanWrapperImpl.
解析表示类的字符串到实际类以及反之。当找不到类时,会抛出 IllegalArgumentException 异常。默认情况下,由 BeanWrapperImpl 注册。

CustomBooleanEditor

Customizable property editor for Boolean properties. By default, registered by BeanWrapperImpl but can be overridden by registering a custom instance of it as a custom editor.
自定义属性编辑器,用于 Boolean 属性。默认情况下,由 BeanWrapperImpl 注册,但可以通过注册自定义实例作为自定义编辑器来覆盖。

CustomCollectionEditor

Property editor for collections, converting any source Collection to a given target Collection type.
集合属性编辑器,将任何源 Collection 转换为指定的目标 Collection 类型。

CustomDateEditor

Customizable property editor for java.util.Date, supporting a custom DateFormat. NOT registered by default. Must be user-registered with the appropriate format as needed.
自定义属性编辑器,支持自定义 DateFormat 。默认未注册。必须按需以适当格式由用户注册。

CustomNumberEditor

Customizable property editor for any Number subclass, such as Integer, Long, Float, or Double. By default, registered by BeanWrapperImpl but can be overridden by registering a custom instance of it as a custom editor.
自定义任何 Number 子类的属性编辑器,例如 IntegerLongFloat ,或 Double 。默认情况下由 BeanWrapperImpl 注册,但可以通过注册自定义实例作为自定义编辑器来覆盖。

FileEditor

Resolves strings to java.io.File objects. By default, registered by BeanWrapperImpl.
解析字符串为 java.io.File 对象。默认情况下,由 BeanWrapperImpl 注册。

InputStreamEditor

One-way property editor that can take a string and produce (through an intermediate ResourceEditor and Resource) an InputStream so that InputStream properties may be directly set as strings. Note that the default usage does not close the InputStream for you. By default, registered by BeanWrapperImpl.
单向属性编辑器,可以接受一个字符串并通过中间的 ResourceEditorResource 生成 InputStream ,以便直接将 InputStream 属性设置为字符串。请注意,默认情况下不会为您关闭 InputStream 。默认情况下,由 BeanWrapperImpl 注册。

LocaleEditor

Can resolve strings to Locale objects and vice-versa (the string format is [language]_[country]_[variant], same as the toString() method of Locale). Also accepts spaces as separators, as an alternative to underscores. By default, registered by BeanWrapperImpl.
可以解析字符串到 Locale 对象,反之亦然(字符串格式为 [language]_[country]_[variant] ,与 LocaletoString() 方法相同)。还接受空格作为分隔符,作为下划线的替代。默认情况下,由 BeanWrapperImpl 注册。

PatternEditor

Can resolve strings to java.util.regex.Pattern objects and vice-versa.
可以将字符串解析为 java.util.regex.Pattern 对象,反之亦然。

PropertiesEditor

Can convert strings (formatted with the format defined in the javadoc of the java.util.Properties class) to Properties objects. By default, registered by BeanWrapperImpl.
可以将字符串(按照 java.util.Properties 类的 javadoc 中定义的格式进行格式化)转换为 Properties 对象。默认情况下,由 BeanWrapperImpl 注册。

StringTrimmerEditor

Property editor that trims strings. Optionally allows transforming an empty string into a null value. NOT registered by default — must be user-registered.
属性编辑器,用于修剪字符串。可选地将空字符串转换为 null 值。默认未注册 — 必须由用户注册。

URLEditor

Can resolve a string representation of a URL to an actual URL object. By default, registered by BeanWrapperImpl.
可以将 URL 的字符串表示形式解析为实际的 URL 对象。默认情况下,由 BeanWrapperImpl 注册。

Spring uses the java.beans.PropertyEditorManager to set the search path for property editors that might be needed. The search path also includes sun.bean.editors, which includes PropertyEditor implementations for types such as Font, Color, and most of the primitive types. Note also that the standard JavaBeans infrastructure automatically discovers PropertyEditor classes (without you having to register them explicitly) if they are in the same package as the class they handle and have the same name as that class, with Editor appended. For example, one could have the following class and package structure, which would be sufficient for the SomethingEditor class to be recognized and used as the PropertyEditor for Something-typed properties.
Spring 使用 java.beans.PropertyEditorManager 来设置可能需要的属性编辑器的搜索路径。搜索路径还包括 sun.bean.editors ,其中包含 PropertyEditor 实现,例如 FontColor 以及大多数原始类型。注意,标准的 JavaBeans 基础设施会自动发现 PropertyEditor 类(无需您显式注册),如果这些类与它们处理的类在同一包中,并且具有相同的名称,且以 Editor 结尾。例如,可以有以下类和包结构,这对于 SomethingEditor 类被识别并用作 PropertyEditorSomething 类型属性就足够了。

com chank pop Something SomethingEditor // the PropertyEditor for the Something class

Note that you can also use the standard BeanInfo JavaBeans mechanism here as well (described to some extent here). The following example uses the BeanInfo mechanism to explicitly register one or more PropertyEditor instances with the properties of an associated class:
请注意,您也可以在这里使用标准的 BeanInfo JavaBeans 机制(部分描述见此处)。以下示例使用 BeanInfo 机制显式注册一个或多个与相关类属性相关的 PropertyEditor 实例:

com chank pop Something SomethingBeanInfo // the BeanInfo for the Something class

The following Java source code for the referenced SomethingBeanInfo class associates a CustomNumberEditor with the age property of the Something class:
以下 Java 源代码将引用的 SomethingBeanInfo 类的 CustomNumberEditor 属性与 Something 类的 age 属性关联:

public class SomethingBeanInfo extends SimpleBeanInfo { public PropertyDescriptor getPropertyDescriptors() { try { final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true); PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Something.class) { @Override public PropertyEditor createPropertyEditor(Object bean) { return numberPE; } }; return new PropertyDescriptor { ageDescriptor }; } catch (IntrospectionException ex) { throw new Error(ex.toString()); } } }
class SomethingBeanInfo : SimpleBeanInfo() { override fun getPropertyDescriptors(): Array<PropertyDescriptor> { try { val numberPE = CustomNumberEditor(Int::class.java, true) val ageDescriptor = object : PropertyDescriptor("age", Something::class.java) { override fun createPropertyEditor(bean: Any): PropertyEditor { return numberPE } } return arrayOf(ageDescriptor) } catch (ex: IntrospectionException) { throw Error(ex.toString()) } } }

(#beans-beans-conversion-customeditor-registration)Registering Additional CustomPropertyEditor Implementations

注册额外的自定义 PropertyEditor 实现

When setting bean properties as string values, a Spring IoC container ultimately uses standard JavaBeans PropertyEditor implementations to convert these strings to the complex type of the property. Spring pre-registers a number of custom PropertyEditor implementations (for example, to convert a class name expressed as a string into a Class object). Additionally, Java’s standard JavaBeans PropertyEditor lookup mechanism lets a PropertyEditor for a class be named appropriately and placed in the same package as the class for which it provides support, so that it can be found automatically.
当将 bean 属性设置为字符串值时,Spring IoC 容器最终使用标准的 JavaBeans PropertyEditor 实现将这些字符串转换为属性的复杂类型。Spring 预先注册了多个自定义 PropertyEditor 实现(例如,将表示为字符串的类名转换为 Class 对象)。此外,Java 的标准 JavaBeans PropertyEditor 查找机制允许为类提供一个适当的名称,并将其放置在与提供支持的类相同的包中,以便可以自动找到。

If there is a need to register other custom PropertyEditors, several mechanisms are available. The most manual approach, which is not normally convenient or recommended, is to use the registerCustomEditor() method of the ConfigurableBeanFactory interface, assuming you have a BeanFactory reference. Another (slightly more convenient) mechanism is to use a special bean factory post-processor called CustomEditorConfigurer. Although you can use bean factory post-processors with BeanFactory implementations, the CustomEditorConfigurer has a nested property setup, so we strongly recommend that you use it with the ApplicationContext, where you can deploy it in similar fashion to any other bean and where it can be automatically detected and applied.
如果需要注册其他自定义 PropertyEditors ,有几种机制可供选择。最手动的方法,通常不便利或不推荐,是使用 ConfigurableBeanFactory 接口的 registerCustomEditor() 方法,假设你有 BeanFactory 引用。另一种(稍微便利一些)的机制是使用名为 CustomEditorConfigurer 的特殊 bean 工厂后处理器。虽然你可以使用与 BeanFactory 实现兼容的 bean 工厂后处理器,但 CustomEditorConfigurer 有嵌套属性设置,所以我们强烈建议你与 ApplicationContext 一起使用,在那里你可以以类似其他 bean 的方式部署它,并且它可以被自动检测和应用。

Note that all bean factories and application contexts automatically use a number of built-in property editors, through their use of a BeanWrapper to handle property conversions. The standard property editors that the BeanWrapper registers are listed in the previous section. Additionally, ApplicationContexts also override or add additional editors to handle resource lookups in a manner appropriate to the specific application context type.
请注意,所有 bean 工厂和应用程序上下文都自动使用一些内置属性编辑器,通过使用 BeanWrapper 来处理属性转换。 BeanWrapper 注册的标准属性编辑器在上一节中列出。此外, ApplicationContext 也覆盖或添加了额外的编辑器,以适当地处理特定应用程序上下文类型的资源查找。

Standard JavaBeans PropertyEditor instances are used to convert property values expressed as strings to the actual complex type of the property. You can use CustomEditorConfigurer, a bean factory post-processor, to conveniently add support for additional PropertyEditor instances to an ApplicationContext.
标准 JavaBeans 实例用于将表示为字符串的属性值转换为属性的真正复杂类型。您可以使用,一个 bean 工厂后处理器,方便地向添加对更多实例的支持。

Consider the following example, which defines a user class called ExoticType and another class called DependsOnExoticType, which needs ExoticType set as a property:
考虑以下示例,它定义了一个名为 ExoticType 的用户类,以及另一个需要将 ExoticType 设置为属性的类 DependsOnExoticType

package example; public class ExoticType { private String name; public ExoticType(String name) { this.name = name; } } public class DependsOnExoticType { private ExoticType type; public void setType(ExoticType type) { this.type = type; } }
package example class ExoticType(val name: String) class DependsOnExoticType { var type: ExoticType? = null }

When things are properly set up, we want to be able to assign the type property as a string, which a PropertyEditor converts into an actual ExoticType instance. The following bean definition shows how to set up this relationship:
当事情设置得当,我们希望能够将类型属性分配为一个字符串,该字符串由 PropertyEditor 转换为实际的 ExoticType 实例。以下 bean 定义显示了如何设置这种关系:

<bean id="sample" class="example.DependsOnExoticType"> <property name="type" value="aNameForExoticType"/> </bean>

The PropertyEditor implementation could look similar to the following:
The PropertyEditor 实现可能看起来类似于以下内容:

// converts string representation to ExoticType object package example; public class ExoticTypeEditor extends PropertyEditorSupport { public void setAsText(String text) { setValue(new ExoticType(text.toUpperCase())); } }
// converts string representation to ExoticType object package example import java.beans.PropertyEditorSupport class ExoticTypeEditor : PropertyEditorSupport() { override fun setAsText(text: String) { value = ExoticType(text.toUpperCase()) } }

Finally, the following example shows how to use CustomEditorConfigurer to register the new PropertyEditor with the ApplicationContext, which will then be able to use it as needed:
最后,以下示例展示了如何使用 CustomEditorConfigurer 将新 PropertyEditor 注册到 ApplicationContext 中,之后就可以根据需要使用它了:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="example.ExoticType" value="example.ExoticTypeEditor"/> </map> </property> </bean>

(#beans-beans-conversion-customeditor-registration-per)UsingPropertyEditorRegistrar 使用

PropertyEditorRegistrar

Another mechanism for registering property editors with the Spring container is to create and use a PropertyEditorRegistrar. This interface is particularly useful when you need to use the same set of property editors in several different situations. You can write a corresponding registrar and reuse it in each case. PropertyEditorRegistrar instances work in conjunction with an interface called PropertyEditorRegistry, an interface that is implemented by the Spring BeanWrapper (and DataBinder). PropertyEditorRegistrar instances are particularly convenient when used in conjunction with CustomEditorConfigurer ( described here), which exposes a property called setPropertyEditorRegistrars(..). PropertyEditorRegistrar instances added to a CustomEditorConfigurer in this fashion can easily be shared with DataBinder and Spring MVC controllers. Furthermore, it avoids the need for synchronization on custom editors: A PropertyEditorRegistrar is expected to create fresh PropertyEditor instances for each bean creation attempt.
另一种将属性编辑器注册到 Spring 容器中的机制是创建并使用一个 PropertyEditorRegistrar 。此接口在需要在不同情况下使用相同的一组属性编辑器时特别有用。您可以编写相应的注册器并在每种情况下重用它。 PropertyEditorRegistrar 实例与一个名为 PropertyEditorRegistry 的接口协同工作,该接口由 Spring BeanWrapper (以及 DataBinder )实现。 PropertyEditorRegistrar 实例在结合使用 CustomEditorConfigurer (在此处描述)时特别方便,它暴露了一个名为 setPropertyEditorRegistrars(..) 的属性。以这种方式添加到 CustomEditorConfigurer 中的 PropertyEditorRegistrar 实例可以轻松与 DataBinder 和 Spring MVC 控制器共享。此外,它避免了在自定义编辑器上同步的需求:预期 PropertyEditorRegistrar 将为每次 bean 创建尝试创建新的 PropertyEditor 实例。

The following example shows how to create your own PropertyEditorRegistrar implementation:
以下示例展示了如何创建自己的 PropertyEditorRegistrar 实现:

package com.foo.editors.spring; public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar { public void registerCustomEditors(PropertyEditorRegistry registry) { // it is expected that new PropertyEditor instances are created registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor()); // you could register as many custom property editors as are required here... } }
package com.foo.editors.spring import org.springframework.beans.PropertyEditorRegistrar import org.springframework.beans.PropertyEditorRegistry class CustomPropertyEditorRegistrar : PropertyEditorRegistrar { override fun registerCustomEditors(registry: PropertyEditorRegistry) { // it is expected that new PropertyEditor instances are created registry.registerCustomEditor(ExoticType::class.java, ExoticTypeEditor()) // you could register as many custom property editors as are required here... } }

See also the org.springframework.beans.support.ResourceEditorRegistrar for an example PropertyEditorRegistrar implementation. Notice how in its implementation of the registerCustomEditors(..) method, it creates new instances of each property editor.
参见 org.springframework.beans.support.ResourceEditorRegistrar 以获取一个 PropertyEditorRegistrar 实现的示例。注意它在实现 registerCustomEditors(..) 方法时,为每个属性编辑器创建了新的实例。

The next example shows how to configure a CustomEditorConfigurer and inject an instance of our CustomPropertyEditorRegistrar into it:
下一个示例展示了如何配置一个 CustomEditorConfigurer 并将其注入我们的 CustomPropertyEditorRegistrar 实例中:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="propertyEditorRegistrars"> <list> <ref bean="customPropertyEditorRegistrar"/> </list> </property> </bean> <bean id="customPropertyEditorRegistrar" class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>

Finally (and in a bit of a departure from the focus of this chapter) for those of you using Spring’s MVC web framework, using a PropertyEditorRegistrar in conjunction with data-binding web controllers can be very convenient. The following example uses a PropertyEditorRegistrar in the implementation of an @InitBinder method:
最后(并且有一点偏离本章的重点)对于使用 Spring MVC Web 框架的各位,结合数据绑定 Web 控制器使用 PropertyEditorRegistrar 可以非常方便。以下示例在实现 @InitBinder 方法时使用了 PropertyEditorRegistrar

@Controller public class RegisterUserController { private final PropertyEditorRegistrar customPropertyEditorRegistrar; RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) { this.customPropertyEditorRegistrar = propertyEditorRegistrar; } @InitBinder void initBinder(WebDataBinder binder) { this.customPropertyEditorRegistrar.registerCustomEditors(binder); } // other methods related to registering a User }
@Controller class RegisterUserController( private val customPropertyEditorRegistrar: PropertyEditorRegistrar) { @InitBinder fun initBinder(binder: WebDataBinder) { this.customPropertyEditorRegistrar.registerCustomEditors(binder) } // other methods related to registering a User }

This style of PropertyEditor registration can lead to concise code (the implementation of the @InitBinder method is only one line long) and lets common PropertyEditor registration code be encapsulated in a class and then shared amongst as many controllers as needed.
这种 PropertyEditor 注册方式可以导致代码简洁( @InitBinder 方法的实现只有一行长)并允许将常见的 PropertyEditor 注册代码封装在类中,然后根据需要共享给尽可能多的控制器。

(#core-convert)3.4. Spring Type Conversion

3.4. Spring 类型转换

Spring 3 introduced a core.convert package that provides a general type conversion system. The system defines an SPI to implement type conversion logic and an API to perform type conversions at runtime. Within a Spring container, you can use this system as an alternative to PropertyEditor implementations to convert externalized bean property value strings to the required property types. You can also use the public API anywhere in your application where type conversion is needed.
Spring 3 引入了一个提供通用类型转换系统的 core.convert 包。该系统定义了一个 SPI 来实现类型转换逻辑,以及一个 API 来在运行时执行类型转换。在 Spring 容器中,您可以使用此系统作为 PropertyEditor 实现的替代方案,将外部化的 Bean 属性值字符串转换为所需的属性类型。您还可以在应用程序中需要类型转换的任何地方使用公共 API。

(#core-convert-Converter-API)3.4.1. Converter SPI 3.4.1. 转换器 SPI

The SPI to implement type conversion logic is simple and strongly typed, as the following interface definition shows:
SPI 实现类型转换逻辑的方式简单且强类型,如下所示接口定义:

package org.springframework.core.convert.converter; public interface Converter<S, T> { T convert(S source); }

To create your own converter, implement the Converter interface and parameterize S as the type you are converting from and T as the type you are converting to. You can also transparently apply such a converter if a collection or array of S needs to be converted to an array or collection of T, provided that a delegating array or collection converter has been registered as well (which DefaultConversionService does by default).
要创建自己的转换器,实现 Converter 接口,并将 S 参数化为要转换的类型,将 T 参数化为要转换到的类型。如果需要将 S 的集合或数组转换为 T 的数组或集合,也可以透明地应用此类转换器,前提是已注册了委托数组或集合转换器( DefaultConversionService 默认执行此操作)。

For each call to convert(S), the source argument is guaranteed to not be null. Your Converter may throw any unchecked exception if conversion fails. Specifically, it should throw an IllegalArgumentException to report an invalid source value. Take care to ensure that your Converter implementation is thread-safe.
对于每次调用 convert(S) ,源参数保证不为 null。如果转换失败,您的 Converter 可能抛出任何未检查的异常。具体来说,它应该抛出 IllegalArgumentException 来报告无效的源值。请确保您的 Converter 实现是线程安全的。

Several converter implementations are provided in the core.convert.support package as a convenience. These include converters from strings to numbers and other common types. The following listing shows the StringToInteger class, which is a typical Converter implementation:
几个转换器实现被提供在 core.convert.support 包中,作为便利。这些包括从字符串到数字和其他常见类型的转换器。以下列表显示了 StringToInteger 类,这是一个典型的 Converter 实现:

package org.springframework.core.convert.support; final class StringToInteger implements Converter<String, Integer> { public Integer convert(String source) { return Integer.valueOf(source); } }

(#core-convert-ConverterFactory-SPI)3.4.2. UsingConverterFactory 3.4.2. 使用ConverterFactory

When you need to centralize the conversion logic for an entire class hierarchy (for example, when converting from String to Enum objects), you can implement ConverterFactory, as the following example shows:
当你需要集中处理整个类层次结构的转换逻辑时(例如,从 String 转换为 Enum 对象时),你可以实现 ConverterFactory ,如下例所示:

package org.springframework.core.convert.converter; public interface ConverterFactory<S, R> { <T extends R> Converter<S, T> getConverter(Class<T> targetType); }

Parameterize S to be the type you are converting from and R to be the base type defining the range of classes you can convert to. Then implement getConverter(Class<T>), where T is a subclass of R.
将 S 参数化为你要转换的类型,将 R 参数化为定义你可以转换到的类范围的基类型。然后实现 getConverter(Class<T>) ,其中 T 是 R 的子类。

Consider the StringToEnumConverterFactory as an example:
考虑 StringToEnumConverterFactory 作为一个例子:

package org.springframework.core.convert.support; final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { return new StringToEnumConverter(targetType); } private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> { private Class<T> enumType; public StringToEnumConverter(Class<T> enumType) { this.enumType = enumType; } public T convert(String source) { return (T) Enum.valueOf(this.enumType, source.trim()); } } }

(#core-convert-GenericConverter-SPI)3.4.3. UsingGenericConverter 3.4.3. 使用GenericConverter

When you require a sophisticated Converter implementation, consider using the GenericConverter interface. With a more flexible but less strongly typed signature than Converter, a GenericConverter supports converting between multiple source and target types. In addition, a GenericConverter makes available source and target field context that you can use when you implement your conversion logic. Such context lets a type conversion be driven by a field annotation or by generic information declared on a field signature.
当您需要复杂的 Converter 实现时,请考虑使用 GenericConverter 接口。与 Converter 相比,它具有更灵活但类型约束较弱的签名,支持在多个源和目标类型之间进行转换。此外, GenericConverter 提供了源和目标字段上下文,您可以在实现转换逻辑时使用这些上下文。这种上下文允许类型转换由字段注释或字段签名上声明的泛型信息驱动。
The following listing shows the interface definition of GenericConverter:
以下列表显示了 GenericConverter 的接口定义:

package org.springframework.core.convert.converter; public interface GenericConverter { public Set<ConvertiblePair> getConvertibleTypes(); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); }

To implement a GenericConverter, have getConvertibleTypes() return the supported source→target type pairs. Then implement convert(Object, TypeDescriptor, TypeDescriptor) to contain your conversion logic. The source TypeDescriptor provides access to the source field that holds the value being converted. The target TypeDescriptor provides access to the target field where the converted value is to be set.
为了实现 GenericConverter ,让 getConvertibleTypes() 返回支持的源→目标类型对。然后实现 convert(Object, TypeDescriptor, TypeDescriptor) 来包含您的转换逻辑。源 TypeDescriptor 提供对包含要转换的值的源字段的访问。目标 TypeDescriptor 提供对要设置转换值的目标字段的访问。

A good example of a GenericConverter is a converter that converts between a Java array and a collection. Such an ArrayToCollectionConverter introspects the field that declares the target collection type to resolve the collection’s element type. This lets each element in the source array be converted to the collection element type before the collection is set on the target field.
一个优秀的 GenericConverter 示例是一个在 Java 数组和集合之间进行转换的转换器。这样的 ArrayToCollectionConverter 会检查声明目标集合类型的字段,以解析集合的元素类型。这允许在将集合设置到目标字段之前,将源数组中的每个元素转换为集合元素类型。

Because GenericConverter is a more complex SPI interface, you should use it only when you need it. Favor Converter or ConverterFactory for basic type conversion needs.
因为 GenericConverter 是一个更复杂的 SPI 接口,您只有在需要时才应使用它。对于基本类型转换需求,优先使用 ConverterConverterFactory

(#core-convert-ConditionalGenericConverter-SPI)UsingConditionalGenericConverter 使用

ConditionalGenericConverter

Sometimes, you want a Converter to run only if a specific condition holds true. For example, you might want to run a Converter only if a specific annotation is present on the target field, or you might want to run a Converter only if a specific method (such as a static valueOf method) is defined on the target class. ConditionalGenericConverter is the union of the GenericConverter and ConditionalConverter interfaces that lets you define such custom matching criteria:
有时,您希望只有当特定条件为真时才运行 Converter 。例如,您可能只想在目标字段上存在特定注释时运行 Converter ,或者您可能只想在目标类上定义了特定方法(如 static valueOf 方法)时运行 ConverterConditionalGenericConverterGenericConverterConditionalConverter 接口的并集,它允许您定义此类自定义匹配标准:

public interface ConditionalConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { }

A good example of a ConditionalGenericConverter is an IdToEntityConverter that converts between a persistent entity identifier and an entity reference. Such an IdToEntityConverter might match only if the target entity type declares a static finder method (for example, findAccount(Long)). You might perform such a finder method check in the implementation of matches(TypeDescriptor, TypeDescriptor).
一个好的 ConditionalGenericConverter 例子是一个 IdToEntityConverter ,它可以在持久化实体标识符和实体引用之间进行转换。这样的 IdToEntityConverter 可能只有在目标实体类型声明了一个静态查找方法(例如, findAccount(Long) )时才会匹配。你可能会在 matches(TypeDescriptor, TypeDescriptor) 的实现中执行这样的查找方法检查。

(#core-convert-ConversionService-API)3.4.4. TheConversionService API 3.4.4. TheConversionService API (注:原文中的“

ConversionService”可能是一个特定标识符或代码,因此未进行翻译。)

ConversionService defines a unified API for executing type conversion logic at runtime. Converters are often run behind the following facade interface:
ConversionService 定义了一个在运行时执行类型转换逻辑的统一 API。转换器通常在以下外观接口后面运行:

package org.springframework.core.convert; public interface ConversionService { boolean canConvert(Class<?> sourceType, Class<?> targetType); <T> T convert(Object source, Class<T> targetType); boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); }

Most ConversionService implementations also implement ConverterRegistry, which provides an SPI for registering converters. Internally, a ConversionService implementation delegates to its registered converters to carry out type conversion logic.
大多数 ConversionService 实现也实现了 ConverterRegistry ,它提供了一个用于注册转换器的 SPI。内部,一个 ConversionService 实现委托给其注册的转换器以执行类型转换逻辑。

A robust ConversionService implementation is provided in the core.convert.support package. GenericConversionService is the general-purpose implementation suitable for use in most environments. ConversionServiceFactory provides a convenient factory for creating common ConversionService configurations.
提供了一个健壮的 ConversionService 实现,包含在 core.convert.support 包中。 GenericConversionService 是通用实现,适用于大多数环境。 ConversionServiceFactory 提供了一个方便的工厂,用于创建常见的 ConversionService 配置。

(#core-convert-Spring-config)3.4.5. Configuring aConversionService 3.4.5. 配置ConversionService

A ConversionService is a stateless object designed to be instantiated at application startup and then shared between multiple threads. In a Spring application, you typically configure a ConversionService instance for each Spring container (or ApplicationContext). Spring picks up that ConversionService and uses it whenever a type conversion needs to be performed by the framework. You can also inject this ConversionService into any of your beans and invoke it directly.
一个 ConversionService 是一个无状态的对象,设计用于在应用程序启动时实例化,然后在多个线程之间共享。在 Spring 应用程序中,您通常为每个 Spring 容器(或 ApplicationContext )配置一个 ConversionService 实例。Spring 会检测到这个 ConversionService 并在框架需要执行类型转换时使用它。您还可以将此 ConversionService 注入到您的任何 bean 中并直接调用它。

If no ConversionService is registered with Spring, the original PropertyEditor-based system is used.
如果没有与 Spring 注册 ConversionService ,则使用基于 PropertyEditor 的原生系统。

To register a default ConversionService with Spring, add the following bean definition with an id of conversionService:
注册默认的 ConversionService 与 Spring,添加以下带有 idconversionService 的 bean 定义:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"/>

A default ConversionService can convert between strings, numbers, enums, collections, maps, and other common types. To supplement or override the default converters with your own custom converters, set the converters property. Property values can implement any of the Converter, ConverterFactory, or GenericConverter interfaces.
默认的 ConversionService 可以转换字符串、数字、枚举、集合、映射和其他常见类型。要使用您自己的自定义转换器补充或覆盖默认转换器,设置 converters 属性。属性值可以实现 ConverterConverterFactoryGenericConverter 接口中的任何一个。

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="example.MyCustomConverter"/> </set> </property> </bean>

It is also common to use a ConversionService within a Spring MVC application. See Conversion and Formatting in the Spring MVC chapter.
在 Spring MVC 应用程序中,也常见使用 ConversionService 。请参阅 Spring MVC 章节中的转换和格式化。

In certain situations, you may wish to apply formatting during conversion. See The FormatterRegistry SPI for details on using FormattingConversionServiceFactoryBean.
在某些情况下,您可能希望在转换过程中应用格式。有关使用 FormattingConversionServiceFactoryBean 的详细信息,请参阅 FormatterRegistry SPI。

(#core-convert-programmatic-usage)3.4.6. Using aConversionService Programmatically

3.4.6. 使用 ConversionService 编程方式

To work with a ConversionService instance programmatically, you can inject a reference to it like you would for any other bean. The following example shows how to do so:
要程序化地与一个 ConversionService 实例一起工作,您可以像对任何其他 bean 一样注入对其的引用。以下示例展示了如何操作:

@Service public class MyService { public MyService(ConversionService conversionService) { this.conversionService = conversionService; } public void doIt() { this.conversionService.convert(...) } }
@Service class MyService(private val conversionService: ConversionService) { fun doIt() { conversionService.convert(...) } }

For most use cases, you can use the convert method that specifies the targetType, but it does not work with more complex types, such as a collection of a parameterized element. For example, if you want to convert a List of Integer to a List of String programmatically, you need to provide a formal definition of the source and target types.
对于大多数用例,您可以使用指定 targetTypeconvert 方法,但它不适用于更复杂的数据类型,例如参数化元素的集合。例如,如果您想将 IntegerList 转换为 StringList ,则需要提供源和目标类型的正式定义。

Fortunately, TypeDescriptor provides various options to make doing so straightforward, as the following example shows:
幸运的是, TypeDescriptor 提供了各种选项,使这样做变得简单,以下示例即可证明:

DefaultConversionService cs = new DefaultConversionService(); List<Integer> input = ... cs.convert(input, TypeDescriptor.forObject(input), // List<Integer> type descriptor TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
val cs = DefaultConversionService() val input: List<Integer> = ... cs.convert(input, TypeDescriptor.forObject(input), // List<Integer> type descriptor TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))

Note that DefaultConversionService automatically registers converters that are appropriate for most environments. This includes collection converters, scalar converters, and basic Object-to-String converters. You can register the same converters with any ConverterRegistry by using the static addDefaultConverters method on the DefaultConversionService class.
请注意, DefaultConversionService 会自动注册适用于大多数环境的转换器。这包括集合转换器、标量转换器和基本的 Object -到- String 转换器。您可以通过使用类 DefaultConversionService 上的静态 addDefaultConverters 方法,以任何 ConverterRegistry 注册相同的转换器。

Converters for value types are reused for arrays and collections, so there is no need to create a specific converter to convert from a Collection of S to a Collection of T, assuming that standard collection handling is appropriate.
值类型转换器被重用于数组集合,因此无需创建特定的转换器将 CollectionS 转换为 CollectionT ,前提是标准集合处理是合适的。

(#format)3.5. Spring Field Formatting

3.5. 春田格式化

As discussed in the previous section, core.convert is a general-purpose type conversion system. It provides a unified ConversionService API as well as a strongly typed Converter SPI for implementing conversion logic from one type to another. A Spring container uses this system to bind bean property values. In addition, both the Spring Expression Language (SpEL) and DataBinder use this system to bind field values. For example, when SpEL needs to coerce a Short to a Long to complete an expression.setValue(Object bean, Object value) attempt, the core.convert system performs the coercion.
如前文所述, core.convert 是一个通用类型转换系统。它提供了一个统一的 ConversionService API,以及一个强类型的 Converter SPI,用于实现从一种类型到另一种类型的转换逻辑。Spring 容器使用此系统来绑定 Bean 属性值。此外,Spring 表达式语言(SpEL)和 DataBinder 也使用此系统来绑定字段值。例如,当 SpEL 需要将 Short 转换为 Long 以完成 expression.setValue(Object bean, Object value) 尝试时, core.convert 系统执行转换。

Now consider the type conversion requirements of a typical client environment, such as a web or desktop application. In such environments, you typically convert from String to support the client postback process, as well as back to String to support the view rendering process. In addition, you often need to localize String values. The more general core.convert Converter SPI does not address such formatting requirements directly. To directly address them, Spring 3 introduced a convenient Formatter SPI that provides a simple and robust alternative to PropertyEditor implementations for client environments.
现在考虑典型客户端环境(如 Web 或桌面应用程序)的类型转换需求。在这些环境中,通常从 String 转换为支持客户端回发过程,以及从 String 转换回来以支持视图渲染过程。此外,您通常需要本地化 String 值。更通用的 core.convert Converter SPI 没有直接解决此类格式化要求。为了直接解决这些问题,Spring 3 引入了一个方便的 Formatter SPI,它为客户端环境提供了一个简单且健壮的替代方案,以替代 PropertyEditor 实现。

In general, you can use the Converter SPI when you need to implement general-purpose type conversion logic — for example, for converting between a java.util.Date and a Long. You can use the Formatter SPI when you work in a client environment (such as a web application) and need to parse and print localized field values. The ConversionService provides a unified type conversion API for both SPIs.
通常,当您需要实现通用类型转换逻辑时,可以使用 Converter SPI,例如在转换 java.util.DateLong 之间。当您在客户端环境(如 Web 应用程序)中工作,需要解析和打印本地化字段值时,可以使用 Formatter SPI。 ConversionService 为这两个 SPI 提供统一的类型转换 API。

(#format-Formatter-SPI)3.5.1. TheFormatter SPI 3.5.1.Formatter SPI

The Formatter SPI to implement field formatting logic is simple and strongly typed. The following listing shows the Formatter interface definition:
SPI 实现字段格式化逻辑的方式简单且类型严格。以下列表显示了接口定义:

package org.springframework.format; public interface Formatter<T> extends Printer<T>, Parser<T> { }

Formatter extends from the Printer and Parser building-block interfaces. The following listing shows the definitions of those two interfaces:
FormatterPrinterParser 建块接口扩展而来。以下列表显示了这两个接口的定义:

public interface Printer<T> { String print(T fieldValue, Locale locale); }
import java.text.ParseException; public interface Parser<T> { T parse(String clientValue, Locale locale) throws ParseException; }

To create your own Formatter, implement the Formatter interface shown earlier. Parameterize T to be the type of object you wish to format — for example, java.util.Date. Implement the print() operation to print an instance of T for display in the client locale. Implement the parse() operation to parse an instance of T from the formatted representation returned from the client locale. Your Formatter should throw a ParseException or an IllegalArgumentException if a parse attempt fails. Take care to ensure that your Formatter implementation is thread-safe.
要创建自己的 Formatter ,实现前面显示的 Formatter 接口。将 T 参数化为您希望格式化的对象类型——例如, java.util.Date 。实现 print() 操作以打印客户端区域设置的 T 实例进行显示。实现 parse() 操作以从客户端区域设置返回的格式化表示中解析 T 实例。如果解析尝试失败,您的 Formatter 应该抛出 ParseExceptionIllegalArgumentException 。请确保您的 Formatter 实现是线程安全的。

The format subpackages provide several Formatter implementations as a convenience. The number package provides NumberStyleFormatter, CurrencyStyleFormatter, and PercentStyleFormatter to format Number objects that use a java.text.NumberFormat. The datetime package provides a DateFormatter to format java.util.Date objects with a java.text.DateFormat.
format 子包提供了一些便利的 Formatter 实现。 number 包提供了 NumberStyleFormatterCurrencyStyleFormatterPercentStyleFormatter 以格式化使用 java.text.NumberFormatNumber 对象。 datetime 包提供了一个用于格式化具有 java.text.DateFormatjava.util.Date 对象的 DateFormatter

The following DateFormatter is an example Formatter implementation:
以下 DateFormatter 是一个示例 Formatter 实现:

package org.springframework.format.datetime; public final class DateFormatter implements Formatter<Date> { private String pattern; public DateFormatter(String pattern) { this.pattern = pattern; } public String print(Date date, Locale locale) { if (date == null) { return ""; } return getDateFormat(locale).format(date); } public Date parse(String formatted, Locale locale) throws ParseException { if (formatted.length() == 0) { return null; } return getDateFormat(locale).parse(formatted); } protected DateFormat getDateFormat(Locale locale) { DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale); dateFormat.setLenient(false); return dateFormat; } }
class DateFormatter(private val pattern: String) : Formatter<Date> { override fun print(date: Date, locale: Locale) = getDateFormat(locale).format(date) @Throws(ParseException::class) override fun parse(formatted: String, locale: Locale) = getDateFormat(locale).parse(formatted) protected fun getDateFormat(locale: Locale): DateFormat { val dateFormat = SimpleDateFormat(this.pattern, locale) dateFormat.isLenient = false return dateFormat } }

The Spring team welcomes community-driven Formatter contributions. See GitHub Issues to contribute.
Spring 团队欢迎社区驱动的 Formatter 贡献。请查看 GitHub 问题进行贡献。

(#format-CustomFormatAnnotations)3.5.2. Annotation-driven Formatting

3.5.2. 注解驱动格式化

Field formatting can be configured by field type or annotation. To bind an annotation to a Formatter, implement AnnotationFormatterFactory. The following listing shows the definition of the AnnotationFormatterFactory interface:
字段格式可以通过字段类型或注解进行配置。要将注解绑定到 Formatter ,请实现 AnnotationFormatterFactory 。以下列表显示了 AnnotationFormatterFactory 接口的定义:

package org.springframework.format; public interface AnnotationFormatterFactory<A extends Annotation> { Set<Class<?>> getFieldTypes(); Printer<?> getPrinter(A annotation, Class<?> fieldType); Parser<?> getParser(A annotation, Class<?> fieldType); }

To create an implementation:
为了创建一个实现:

  1. Parameterize A to be the field annotationType with which you wish to associate formatting logic — for example org.springframework.format.annotation.DateTimeFormat.
    参数化 A 为要关联格式化逻辑的字段 annotationType — 例如 org.springframework.format.annotation.DateTimeFormat

  2. Have getFieldTypes() return the types of fields on which the annotation can be used.
    返回可以应用注解的字段类型。

  3. Have getPrinter() return a Printer to print the value of an annotated field.
    getPrinter() 返回一个 Printer 以打印注释字段的值。

  4. Have getParser() return a Parser to parse a clientValue for an annotated field.
    getParser() 返回一个 Parser 解析一个注解字段的 clientValue

The following example AnnotationFormatterFactory implementation binds the @NumberFormat annotation to a formatter to let a number style or pattern be specified:
以下示例 AnnotationFormatterFactory 实现将 @NumberFormat 注解绑定到格式化器,以便指定数字样式或模式:

public final class NumberFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<NumberFormat> { public Set<Class<?>> getFieldTypes() { return new HashSet<Class<?>>(asList(new Class<?> { Short.class, Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, BigInteger.class })); } public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) { return configureFormatterFrom(annotation, fieldType); } public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) { return configureFormatterFrom(annotation, fieldType); } private Formatter<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) { if (!annotation.pattern().isEmpty()) { return new NumberStyleFormatter(annotation.pattern()); } else { Style style = annotation.style(); if (style == Style.PERCENT) { return new PercentStyleFormatter(); } else if (style == Style.CURRENCY) { return new CurrencyStyleFormatter(); } else { return new NumberStyleFormatter(); } } } }
class NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> { override fun getFieldTypes(): Set<Class<*>> { return setOf(Short::class.java, Int::class.java, Long::class.java, Float::class.java, Double::class.java, BigDecimal::class.java, BigInteger::class.java) } override fun getPrinter(annotation: NumberFormat, fieldType: Class<*>): Printer<Number> { return configureFormatterFrom(annotation, fieldType) } override fun getParser(annotation: NumberFormat, fieldType: Class<*>): Parser<Number> { return configureFormatterFrom(annotation, fieldType) } private fun configureFormatterFrom(annotation: NumberFormat, fieldType: Class<*>): Formatter<Number> { return if (annotation.pattern.isNotEmpty()) { NumberStyleFormatter(annotation.pattern) } else { val style = annotation.style when { style === NumberFormat.Style.PERCENT -> PercentStyleFormatter() style === NumberFormat.Style.CURRENCY -> CurrencyStyleFormatter() else -> NumberStyleFormatter() } } } }

To trigger formatting, you can annotate fields with @NumberFormat, as the following example shows:
要触发格式化,您可以使用@NumberFormat 注解字段,如下例所示:

public class MyModel { @NumberFormat(style=Style.CURRENCY) private BigDecimal decimal; }
class MyModel( @field:NumberFormat(style = Style.CURRENCY) private val decimal: BigDecimal )

(#format-annotations-api)Format Annotation API 格式注解 API

A portable format annotation API exists in the org.springframework.format.annotation package. You can use @NumberFormat to format Number fields such as Double and Long, and @DateTimeFormat to format java.util.Date, java.util.Calendar, Long (for millisecond timestamps) as well as JSR-310 java.time.
一个便携式格式化注释 API 存在于 org.springframework.format.annotation 包中。您可以使用 @NumberFormat 来格式化 Number 字段,例如 DoubleLong ,以及 @DateTimeFormat 来格式化 java.util.Datejava.util.CalendarLong (用于毫秒时间戳)以及 JSR-310 java.time

The following example uses @DateTimeFormat to format a java.util.Date as an ISO Date (yyyy-MM-dd):
以下示例使用 @DateTimeFormat 格式化 java.util.Date 为 ISO 日期(yyyy-MM-dd):

public class MyModel { @DateTimeFormat(iso=ISO.DATE) private Date date; }
class MyModel( @DateTimeFormat(iso=ISO.DATE) private val date: Date )

(#format-FormatterRegistry-SPI)3.5.3. TheFormatterRegistry SPI 3.5.3.FormatterRegistry SPI

The FormatterRegistry is an SPI for registering formatters and converters. FormattingConversionService is an implementation of FormatterRegistry suitable for most environments. You can programmatically or declaratively configure this variant as a Spring bean, e.g. by using FormattingConversionServiceFactoryBean. Because this implementation also implements ConversionService, you can directly configure it for use with Spring’s DataBinder and the Spring Expression Language (SpEL).
FormatterRegistry 是用于注册格式化和转换器的 SPI。 FormattingConversionServiceFormatterRegistry 的实现,适用于大多数环境。您可以通过编程或声明性配置此变体作为 Spring bean,例如使用 FormattingConversionServiceFactoryBean 。因为此实现还实现了 ConversionService ,所以您可以直接将其配置为与 Spring 的 DataBinder 和 Spring 表达式语言 (SpEL) 一起使用。

The following listing shows the FormatterRegistry SPI:
以下列表显示了 FormatterRegistry SPI:

package org.springframework.format; public interface FormatterRegistry extends ConverterRegistry { void addPrinter(Printer<?> printer); void addParser(Parser<?> parser); void addFormatter(Formatter<?> formatter); void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser); void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory); }

As shown in the preceding listing, you can register formatters by field type or by annotation.

The FormatterRegistry SPI lets you configure formatting rules centrally, instead of duplicating such configuration across your controllers. For example, you might want to enforce that all date fields are formatted a certain way or that fields with a specific annotation are formatted in a certain way.
FormatterRegistry SPI 允许您集中配置格式化规则,而不是在您的控制器之间重复此类配置。例如,您可能希望强制所有日期字段以某种方式格式化,或者特定注释的字段以某种方式格式化。
With a shared FormatterRegistry, you define these rules once, and they are applied whenever formatting is needed.
使用共享 FormatterRegistry ,您只需定义一次这些规则,每当需要格式化时都会应用它们。

(#format-FormatterRegistrar-SPI)3.5.4. TheFormatterRegistrar SPI 3.5.4.FormatterRegistrar SPI

FormatterRegistrar is an SPI for registering formatters and converters through the FormatterRegistry. The following listing shows its interface definition:
FormatterRegistrar 是通过 FormatterRegistry 注册格式化和转换器的 SPI。以下列表显示了其接口定义:

package org.springframework.format; public interface FormatterRegistrar { void registerFormatters(FormatterRegistry registry); }

A FormatterRegistrar is useful when registering multiple related converters and formatters for a given formatting category, such as date formatting.
一个 FormatterRegistrar 在注册多个相关转换器和格式化程序时很有用,例如日期格式化。
It can also be useful where declarative registration is insufficient — for example, when a formatter needs to be indexed under a specific field type different from its own <T> or when registering a Printer/Parser pair. The next section provides more information on converter and formatter registration.
它也可以在声明性注册不足的情况下很有用——例如,当格式化器需要在其自身的 <T> 或不同的特定字段类型下进行索引时,或者注册一个 Printer / Parser 对时。下一节提供了有关转换器和格式化器注册的更多信息。

(#format-configuring-formatting-mvc)3.5.5. Configuring Formatting in Spring MVC

3.5.5. 在 Spring MVC 中配置格式化

(#format-configuring-formatting-globaldatetimeformat)3.6. Configuring a Global Date and Time Format

3.6. 配置全局日期和时间格式

By default, date and time fields not annotated with @DateTimeFormat are converted from strings by using the DateFormat.SHORT style. If you prefer, you can change this by defining your own global format.
默认情况下,未使用 @DateTimeFormat 注解的日期和时间字段将使用 DateFormat.SHORT 样式转换为字符串。如果您愿意,可以通过定义自己的全局格式来更改此设置。

To do that, ensure that Spring does not register default formatters. Instead, register formatters manually with the help of:
为了做到这一点,请确保 Spring 不注册默认格式化程序。相反,使用以下帮助手动注册格式化程序:

  • org.springframework.format.datetime.standard.DateTimeFormatterRegistrar

  • org.springframework.format.datetime.DateFormatterRegistrar

For example, the following Java configuration registers a global yyyyMMdd format:
例如,以下 Java 配置注册了一个全局 yyyyMMdd 格式:

@Configuration public class AppConfig { @Bean public FormattingConversionService conversionService() { // Use the DefaultFormattingConversionService but do not register defaults DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); // Ensure @NumberFormat is still supported conversionService.addFormatterForFieldAnnotation( new NumberFormatAnnotationFormatterFactory()); // Register JSR-310 date conversion with a specific global format DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar(); dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd")); dateTimeRegistrar.registerFormatters(conversionService); // Register date conversion with a specific global format DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar(); dateRegistrar.setFormatter(new DateFormatter("yyyyMMdd")); dateRegistrar.registerFormatters(conversionService); return conversionService; } }
@Configuration class AppConfig { @Bean fun conversionService(): FormattingConversionService { // Use the DefaultFormattingConversionService but do not register defaults return DefaultFormattingConversionService(false).apply { // Ensure @NumberFormat is still supported addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory()) // Register JSR-310 date conversion with a specific global format val dateTimeRegistrar = DateTimeFormatterRegistrar() dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd")) dateTimeRegistrar.registerFormatters(this) // Register date conversion with a specific global format val dateRegistrar = DateFormatterRegistrar() dateRegistrar.setFormatter(DateFormatter("yyyyMMdd")) dateRegistrar.registerFormatters(this) } } }

If you prefer XML-based configuration, you can use a FormattingConversionServiceFactoryBean. The following example shows how to do so:
如果您偏好基于 XML 的配置,可以使用 FormattingConversionServiceFactoryBean 。以下示例展示了如何操作:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="registerDefaultFormatters" value="false" /> <property name="formatters"> <set> <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" /> </set> </property> <property name="formatterRegistrars"> <set> <bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar"> <property name="dateFormatter"> <bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean"> <property name="pattern" value="yyyyMMdd"/> </bean> </property> </bean> </set> </property> </bean> </beans>

(#validation-beanvalidation)3.7. Java Bean Validation

3.7. Java Bean 验证

The Spring Framework provides support for the Java Bean Validation API.
Spring 框架为 Java Bean 验证 API 提供支持。

(#validation-beanvalidation-overview)3.7.1. Overview of Bean Validation

3.7.1. Bean 验证概述

Bean Validation provides a common way of validation through constraint declaration and metadata for Java applications. To use it, you annotate domain model properties with declarative validation constraints which are then enforced by the runtime.
Bean Validation 通过约束声明和元数据为 Java 应用程序提供了一种通用的验证方式。要使用它,您需要使用声明性验证约束注解领域模型属性,然后由运行时强制执行。
There are built-in constraints, and you can also define your own custom constraints.
存在内置约束,您还可以定义自己的自定义约束。

Consider the following example, which shows a simple PersonForm model with two properties:
考虑以下示例,它展示了一个具有两个属性的简单 PersonForm 模型:

public class PersonForm { private String name; private int age; }
class PersonForm( private val name: String, private val age: Int )

Bean Validation lets you declare constraints as the following example shows:
Bean Validation 允许您按照以下示例声明约束:

public class PersonForm { @NotNull @Size(max=64) private String name; @Min(0) private int age; }
class PersonForm( @get:NotNull @get:Size(max=64) private val name: String, @get:Min(0) private val age: Int )

A Bean Validation validator then validates instances of this class based on the declared constraints. See Bean Validation for general information about the API. See the Hibernate Validator documentation for specific constraints. To learn how to set up a bean validation provider as a Spring bean, keep reading.
一个 Bean 验证验证器随后根据声明的约束验证此类实例。有关 API 的一般信息,请参阅 Bean 验证。有关特定约束的详细信息,请参阅 Hibernate Validator 文档。要了解如何将 Bean 验证提供者设置为 Spring bean,请继续阅读。

(#validation-beanvalidation-spring)3.7.2. Configuring a Bean Validation Provider

3.7.2. 配置 Bean 验证提供程序

Spring provides full support for the Bean Validation API including the bootstrapping of a Bean Validation provider as a Spring bean. This lets you inject a javax.validation.ValidatorFactory or javax.validation.Validator wherever validation is needed in your application.
Spring 提供了对 Bean Validation API 的全面支持,包括将 Bean Validation 提供者作为 Spring bean 进行引导。这使得您可以在应用程序中需要验证的地方注入 javax.validation.ValidatorFactoryjavax.validation.Validator

You can use the LocalValidatorFactoryBean to configure a default Validator as a Spring bean, as the following example shows:
您可以使用 LocalValidatorFactoryBean 来配置一个默认的 Validator 作为 Spring Bean,如下例所示:

import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @Configuration public class AppConfig { @Bean public LocalValidatorFactoryBean validator() { return new LocalValidatorFactoryBean(); } }
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

The basic configuration in the preceding example triggers bean validation to initialize by using its default bootstrap mechanism. A Bean Validation provider, such as the Hibernate Validator, is expected to be present in the classpath and is automatically detected.
前一个示例中的基本配置通过使用其默认的引导机制触发 Bean 验证初始化。期望在类路径中存在一个 Bean 验证提供者,例如 Hibernate Validator,并且会自动检测。

(#validation-beanvalidation-spring-inject)Injecting a Validator 注入验证器

LocalValidatorFactoryBean implements both javax.validation.ValidatorFactory and javax.validation.Validator, as well as Spring’s org.springframework.validation.Validator. You can inject a reference to either of these interfaces into beans that need to invoke validation logic.
LocalValidatorFactoryBean 实现了 javax.validation.ValidatorFactoryjavax.validation.Validator ,以及 Spring 的 org.springframework.validation.Validator 。您可以将这些接口之一的引用注入到需要调用验证逻辑的 bean 中。

You can inject a reference to javax.validation.Validator if you prefer to work with the Bean Validation API directly, as the following example shows:
您可以选择直接使用 Bean Validation API,如下例所示,注入对 javax.validation.Validator 的引用:

import javax.validation.Validator; @Service public class MyService { @Autowired private Validator validator; }
import javax.validation.Validator; @Service class MyService(@Autowired private val validator: Validator)

You can inject a reference to org.springframework.validation.Validator if your bean requires the Spring Validation API, as the following example shows:
您可以在您的 bean 需要 Spring 验证 API 的情况下注入对 org.springframework.validation.Validator 的引用,如下例所示:

import org.springframework.validation.Validator; @Service public class MyService { @Autowired private Validator validator; }
import org.springframework.validation.Validator @Service class MyService(@Autowired private val validator: Validator)

(#validation-beanvalidation-spring-constraints)Configuring Custom Constraints

配置自定义约束

Each bean validation constraint consists of two parts:
每个 Bean 验证约束由两部分组成:

  • A @Constraint annotation that declares the constraint and its configurable properties.
    一个声明约束及其可配置属性的 @Constraint 注释。

  • An implementation of the javax.validation.ConstraintValidator interface that implements the constraint’s behavior.
    一个实现约束行为 javax.validation.ConstraintValidator 接口的实现。

To associate a declaration with an implementation, each @Constraint annotation references a corresponding ConstraintValidator implementation class. At runtime, a ConstraintValidatorFactory instantiates the referenced implementation when the constraint annotation is encountered in your domain model.
为了将声明与实现关联起来,每个 @Constraint 注解引用相应的 ConstraintValidator 实现类。在运行时,当在您的领域模型中遇到约束注解时, ConstraintValidatorFactory 会实例化引用的实现。

By default, the LocalValidatorFactoryBean configures a SpringConstraintValidatorFactory that uses Spring to create ConstraintValidator instances. This lets your custom ConstraintValidators benefit from dependency injection like any other Spring bean.
默认情况下, LocalValidatorFactoryBean 配置了一个使用 Spring 创建 ConstraintValidator 实例的 SpringConstraintValidatorFactory 。这使得您的自定义 ConstraintValidators 可以像任何其他 Spring bean 一样受益于依赖注入。

The following example shows a custom @Constraint declaration followed by an associated ConstraintValidator implementation that uses Spring for dependency injection:
以下示例展示了自定义 @Constraint 声明,随后是相关的 ConstraintValidator 实现,该实现使用 Spring 进行依赖注入:

@Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy=MyConstraintValidator.class) public @interface MyConstraint { }
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD) @Retention(AnnotationRetention.RUNTIME) @Constraint(validatedBy = MyConstraintValidator::class) annotation class MyConstraint
import javax.validation.ConstraintValidator; public class MyConstraintValidator implements ConstraintValidator { @Autowired; private Foo aDependency; // ... }
import javax.validation.ConstraintValidator class MyConstraintValidator(private val aDependency: Foo) : ConstraintValidator { // ... }

As the preceding example shows, a ConstraintValidator implementation can have its dependencies @Autowired as any other Spring bean.
正如前面的示例所示,一个 ConstraintValidator 实现可以将其依赖 @Autowired 视为任何其他 Spring bean。

(#validation-beanvalidation-spring-method)Spring-driven Method Validation

弹簧驱动的方法验证

You can integrate the method validation feature supported by Bean Validation 1.1 (and, as a custom extension, also by Hibernate Validator 4.3) into a Spring context through a MethodValidationPostProcessor bean definition:
您可以将由 Bean Validation 1.1(以及作为自定义扩展的 Hibernate Validator 4.3)支持的验证方法功能集成到 Spring 上下文中,通过 MethodValidationPostProcessor bean 定义:

import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; @Configuration public class AppConfig { @Bean public MethodValidationPostProcessor validationPostProcessor() { return new MethodValidationPostProcessor(); } }
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

To be eligible for Spring-driven method validation, all target classes need to be annotated with Spring’s @Validated annotation, which can optionally also declare the validation groups to use. See MethodValidationPostProcessor for setup details with the Hibernate Validator and Bean Validation 1.1 providers.
要符合 Spring 驱动的验证方法,所有目标类都需要使用 Spring 的 @Validated 注解进行标注,该注解还可以选择性地声明要使用的验证组。有关使用 Hibernate Validator 和 Bean Validation 1.1 提供程序的设置详细信息,请参阅 MethodValidationPostProcessor

Method validation relies on AOP Proxies around the target classes, either JDK dynamic proxies for methods on interfaces or CGLIB proxies. There are certain limitations with the use of proxies, some of which are described in Understanding AOP Proxies. In addition remember to always use methods and accessors on proxied classes; direct field access will not work.
方法验证依赖于围绕目标类的 AOP 代理,无论是接口上的 JDK 动态代理还是 CGLIB 代理。使用代理存在某些限制,其中一些在《理解 AOP 代理》中有所描述。此外,请记住始终在代理类上使用方法和访问器;直接字段访问将不起作用。

(#validation-beanvalidation-spring-other)Additional Configuration Options

附加配置选项

The default LocalValidatorFactoryBean configuration suffices for most cases. There are a number of configuration options for various Bean Validation constructs, from message interpolation to traversal resolution. See the LocalValidatorFactoryBean javadoc for more information on these options.
默认的 LocalValidatorFactoryBean 配置适用于大多数情况。对于各种 Bean Validation 构造,有许多配置选项,从消息插值到遍历解析。有关这些选项的更多信息,请参阅 LocalValidatorFactoryBean javadoc。

(#validation-binder)3.7.3. Configuring aDataBinder 3.7.3. 配置DataBinder

Since Spring 3, you can configure a DataBinder instance with a Validator. Once configured, you can invoke the Validator by calling binder.validate(). Any validation Errors are automatically added to the binder’s BindingResult.
自 Spring 3 以来,您可以使用 DataBinder 实例与 Validator 进行配置。一旦配置完成,您可以通过调用 binder.validate() 来调用 Validator 。任何验证 Errors 都会自动添加到绑定器的 BindingResult 中。

The following example shows how to use a DataBinder programmatically to invoke validation logic after binding to a target object:
以下示例展示了如何使用 DataBinder 在绑定到目标对象后程序化地调用验证逻辑:

Foo target = new Foo(); DataBinder binder = new DataBinder(target); binder.setValidator(new FooValidator()); // bind to the target object binder.bind(propertyValues); // validate the target object binder.validate(); // get BindingResult that includes any validation errors BindingResult results = binder.getBindingResult();
val target = Foo() val binder = DataBinder(target) binder.validator = FooValidator() // bind to the target object binder.bind(propertyValues) // validate the target object binder.validate() // get BindingResult that includes any validation errors val results = binder.bindingResult

You can also configure a DataBinder with multiple Validator instances through dataBinder.addValidators and dataBinder.replaceValidators. This is useful when combining globally configured bean validation with a Spring Validator configured locally on a DataBinder instance. See Spring MVC Validation Configuration.
您还可以通过 dataBinder.addValidatorsdataBinder.replaceValidators 配置多个 Validator 实例的 DataBinder 。当将全局配置的 Bean 验证与在 DataBinder 实例上本地配置的 Spring Validator 结合使用时,这很有用。请参阅 Spring MVC 验证配置。

(#validation-mvc)3.7.4. Spring MVC 3 Validation

3.7.4. Spring MVC 3 验证

See Validation in the Spring MVC chapter.
查看 Spring MVC 章节中的验证。

ON THIS PAGE