Data Binding

数据绑定对于将用户输入绑定到目标对象很有用,其中用户输入是包含属性路径作为密钥的映射,遵循 [JavaBeans 约定,beans-beans-conventions]DataBinder 是支持此功能的主要类,它提供两种绑定用户输入的方法:

Data binding is useful for binding user input to a target object where user input is a map with property paths as keys, following beans-beans-conventions. DataBinder is the main class that supports this, and it provides two ways to bind user input:

  • beans-constructor-binding - bind user input to a public data constructor, looking up constructor argument values in the user input.

  • beans-beans - bind user input to setters, matching keys from the user input to properties of the target object structure.

可以应用构造函数和属性绑定,也可以仅应用其中一种绑定。

You can apply both constructor and property binding or only one.

Constructor Binding

要使用构造函数绑定:

To use constructor binding:

  1. Create a DataBinder with null as the target object.

  2. Set targetType to the target class.

  3. Call construct.

目标类应该具有一个公共构造函数或一个具有参数的非公共构造函数。如果有多个构造函数,则在存在的情况下使用默认构造函数。

The target class should have a single public constructor or a single non-public constructor with arguments. If there are multiple constructors, then a default constructor if present is used.

默认情况下,参数值是通过构造函数参数名称查找的。如果存在,Spring MVC 和 WebFlux 通过构造函数参数或字段上的 @BindParam 注解支持自定义名称映射。如有必要,还可以在 DataBinder 上配置 NameResolver,以自定义要使用的参数名称。

By default, argument values are looked up via constructor parameter names. Spring MVC and WebFlux support a custom name mapping through the @BindParam annotation on constructor parameters or fields if present. If necessary, you can also configure a NameResolver on DataBinder to customize the argument name to use.

根据需要应用 [类型转换,beans-beans-conventions] 来转换用户输入。如果构造函数参数是对象,则以相同方式递归构造它,但通过嵌套属性路径。这意味着构造函数绑定既创建目标对象又创建其包含的任何对象。

beans-beans-conventions is applied as needed to convert user input. If the constructor parameter is an object, it is constructed recursively in the same manner, but through a nested property path. That means constructor binding creates both the target object and any objects it contains.

构造函数绑定支持`List`、Map`和数组参数,这些参数从单个字符串(例如,逗号分隔的列表)转换而来,或者基于索引键(例如`accounts[2].name`或`account[KEY].name)。

Constructor binding supports List, Map, and array arguments either converted from a single string, e.g. comma-separated list, or based on indexed keys such as accounts[2].name or account[KEY].name.

绑定和转换错误在 DataBinderBindingResult 中反映出来。如果成功创建目标,则在调用 construct 之后将 target 设置为创建的实例。

Binding and conversion errors are reflected in the BindingResult of the DataBinder. If the target is created successfully, then target is set to the created instance after the call to construct.

Property Binding with BeanWrapper

`org.springframework.beans`包遵守 JavaBeans 标准。JavaBean 是一个类,它有一个默认的无参数构造函数,并遵循一个命名约定,其中(例如)名为`bingoMadness`的属性将有一个名为`setBingoMadness(..)`的 setter 方法和一个名为`getBingoMadness()`的 getter 方法。有关 JavaBeans 和规范的更多信息,请参见https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/beans/package-summary.html[javabeans]。

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.

beans 包中一个非常重要的类是 BeanWrapper 接口及其对应实现 (BeanWrapperImpl)。正如 javadoc 中所述,BeanWrapper 提供了设置和获取属性值(单个或批量)、获取属性描述符以及查询属性以确定它们是否可读或可写。此外,BeanWrapper 还支持嵌套属性,使属性设置能够设置无限深度的子属性。BeanWrapper 还支持添加标准 JavaBean PropertyChangeListenersVetoableChangeListeners,而无需在目标类中提供支持代码。最后但并非最不重要的一点是,BeanWrapper 提供了设置索引属性的支持。通常不由应用程序代码直接使用 BeanWrapper,而由 DataBinderBeanFactory 使用。

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.

BeanWrapper 的工作方式部分由其名称指示:它包装一个 Bean 以对该 Bean 执行操作,例如设置和检索属性。

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.

Setting and Getting Basic and Nested Properties

设置和获取属性是通过 BeanWrappersetPropertyValuegetPropertyValue 重载方法变体完成的。有关详细信息,请参见其 Javadoc。下表显示了这些约定的示例:

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:

Table 1. Examples of properties
Expression Explanation

name

Indicates the property name that corresponds to the getName() or isName() and setName(..) methods.

account.name

Indicates the nested property name of the property account that corresponds to (for example) the getAccount().setName() or getAccount().getName() methods.

accounts[2]

Indicates the third element of the indexed property account. Indexed properties can be of type array, list, or other naturally ordered collection.

accounts[KEY]

Indicates the value of the map entry indexed by the KEY value.

(如果您不打算直接使用 BeanWrapper,那么本节对您来说并不重要。如果你只使用 DataBinderBeanFactory 以及它们的默认实现,你应该跳到 xref:core/validation/beans-beans.adoc#beans-beans-conversion[section on PropertyEditors。)

(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 获取和设置属性:

The following two example classes use the BeanWrapper to get and set properties:

  • Java

  • Kotlin

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
}
  • Java

  • Kotlin

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
}

以下代码段展示了一些有关如何检索和操作实例化的 CompanyEmployee 的属性的示例:

The following code snippets show some examples of how to retrieve and manipulate some of the properties of instantiated `Company`s and `Employee`s:

  • Java

  • Kotlin

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?

`PropertyEditor’s

Spring 使用`PropertyEditor`的概念来实现`Object`和`String`之间的转换。用一种与对象自身不同的方式表示属性可能会很方便。例如,一个`Date`可以用一种人类可读的方式来表示(像`String`一样:‘2007-14-09'),同时我们仍然可以将人类可读的形式转换为原始日期(或者,更好的是,可以将以人类可读的形式输入的任何日期转换为`Date`对象)。可以通过注册类型为`java.beans.PropertyEditor`的自定义编辑器来实现此行为。在`BeanWrapper`中注册自定义编辑器,或在特定的 IoC 容器中(如前一章中所述),可以使它了解如何将属性转换为所需的类型。有关`PropertyEditor`的更多信息,请参见https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/beans/package-summary.html[Oracle 中`java.beans`包的 javadoc]。

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 中使用属性编辑功能的一些示例:

A couple of examples where property editing is used in 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.

  • 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 提供了大量内置的 PropertyEditor 实现来简化生活。它们都位于 org.springframework.beans.propertyeditors 包中。大多数(但并非全部,如下表所示)默认情况下均由 BeanWrapperImpl 注册。在某些方面可以配置属性编辑器的情况下,你仍可以注册自己的变体来覆盖默认变体。下表描述了 Spring 提供的各种 PropertyEditor 实现:

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:

Table 2. Built-in PropertyEditor Implementations
Class Explanation

ByteArrayPropertyEditor

Editor for byte arrays. Converts strings to their corresponding byte representations. Registered by default by 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.

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.

CustomCollectionEditor

Property editor for collections, converting any source Collection to a given target Collection type.

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.

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.

FileEditor

Resolves strings to java.io.File objects. By default, registered by 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.

LocaleEditor

Can resolve strings to Locale objects and vice-versa (the string format is [country][variant], same as the toString() method of Locale). Also accepts spaces as separators, as an alternative to underscores. By default, registered by BeanWrapperImpl.

PatternEditor

Can resolve strings to java.util.regex.Pattern objects and vice-versa.

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.

StringTrimmerEditor

Property editor that trims strings. Optionally allows transforming an empty string into a null value. NOT registered by default — must be user-registered.

URLEditor

Can resolve a string representation of a URL to an actual URL object. By default, registered by BeanWrapperImpl.

Spring 使用 java.beans.PropertyEditorManager 来设置可能需要的属性编辑器的搜索路径。搜索路径还包括 sun.bean.editors,其中包含类型如 FontColor 和大多数原始类型这样的 PropertyEditor 实现。还需要注意的是,标准 JavaBean 基础设施如果在与其处理的类位于同一包中且具有相同名称(使用添加的 Editor)的情况下会自动发现 PropertyEditor 类(无需明确注册它们)。例如,可以有以下类和包结构,这将足以使 SomethingEditor 类被识别并用作 Something 类型属性的 PropertyEditor

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.

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

请注意,您还可以在此处使用标准`BeanInfo`JavaBeans 机制(某种程度上已在此处https://docs.oracle.com/javase/tutorial/javabeans/advanced/customization.html[描述])。以下示例使用`BeanInfo`机制将一个或多个`PropertyEditor`实例显式地注册到关联类的属性中:

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:

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

引用 SomethingBeanInfo 类的以下 Java 源代码将 CustomNumberEditorSomething 类的 age 属性相关联:

The following Java source code for the referenced SomethingBeanInfo class associates a CustomNumberEditor with the age property of the Something class:

  • Java

  • Kotlin

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())
		}

	}
}

Custom `PropertyEditor’s

在将 bean 属性设为字符串值时,Spring IoC 容器最终使用标准 JavaBean PropertyEditor 实现将这些字符串转换为属性的复杂类型。Spring 预先注册了大量自定义 PropertyEditor 实现(例如,将表示为字符串的类名转换为 Class 对象)。此外,Java 的标准 JavaBean PropertyEditor 查找机制允许对类的 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.

如果需要注册其他自定义 PropertyEditors,则可以采用几种机制。最手动的(通常不方便又不推荐)的方法是使用 ConfigurableBeanFactory 接口的 registerCustomEditor() 方法,假设你具有 BeanFactory 引用。另一种(稍微方便一些)的方法是使用称为 CustomEditorConfigurer 的特殊 bean 工厂后处理器。虽然你可以将 bean 工厂后处理器与 BeanFactory 实现配合使用,但 CustomEditorConfigurer 有一个嵌套的属性设置,因此我们强烈建议你将其与 ApplicationContext 一起使用,你可以在其中以类似于任何其他 bean 的方式部署它,并且可以在其中自动检测并应用它。

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.

请注意,所有 Bean 工厂和应用程序上下文都通过使用 BeanWrapper 来处理属性转换,从而自动使用许多内置的属性编辑器。BeanWrapper 注册的标准属性编辑器列在 previous section 中。此外,ApplicationContext 还将覆盖或添加其他编辑器,以便以适合特定应用程序上下文类型的方式处理资源查找。

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, `ApplicationContext`s also override or add additional editors to handle resource lookups in a manner appropriate to the specific application context type.

标准的 JavaBean PropertyEditor 实例用于将表示为字符串的属性值转换为属性的实际复杂类型。你可以使用 CustomEditorConfigurer,即一个 bean 工厂后处理器,向 ApplicationContext 方便地添加对其他 PropertyEditor 实例的支持。

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.

考虑以下示例,它定义了一个名为 ExoticType 的用户类和另一个名为 DependsOnExoticType 的类,该类需要将 ExoticType 设置为一个属性:

Consider the following example, which defines a user class called ExoticType and another class called DependsOnExoticType, which needs ExoticType set as a property:

  • Java

  • Kotlin

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;
	}
}
class ExoticType(val name: String)

class DependsOnExoticType {

	var type: ExoticType? = null
}

在正确设置好一切后,我们想能够将类型属性指定为一个字符串,PropertyEditor 会将其转换为一个实际的 ExoticType 实例。以下 bean 定义显示了如何设置此关系:

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:

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

PropertyEditor 实现可能看起来如下:

The PropertyEditor implementation could look similar to the following:

  • Java

  • Kotlin

import java.beans.PropertyEditorSupport;

// converts string representation to ExoticType object
public class ExoticTypeEditor extends PropertyEditorSupport {

	public void setAsText(String text) {
		setValue(new ExoticType(text.toUpperCase()));
	}
}
import java.beans.PropertyEditorSupport

// converts string representation to ExoticType object
class ExoticTypeEditor : PropertyEditorSupport() {

	override fun setAsText(text: String) {
		value = ExoticType(text.toUpperCase())
	}
}

最后,以下示例显示了如何使用 CustomEditorConfigurer 使用新 PropertyEditor 注册 ApplicationContext,然后根据需要使用它:

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:

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

PropertyEditorRegistrar

向 Spring 容器注册属性编辑器的另一种机制是创建并使用 PropertyEditorRegistrar。当需要在多个不同情况下使用同一组属性编辑器时,此接口特别有用。你可以编写一个相应的注册器,并将其在每种情况下重复使用。PropertyEditorRegistrar 实例与由 Spring BeanWrapper(和 DataBinder)实现的称为 PropertyEditorRegistry 的接口一起使用。PropertyEditorRegistrar 实例与 CustomEditorConfigurer 配合使用时非常方便(在 here 中已描述),它公开了称为 setPropertyEditorRegistrars(..) 的属性。以这种方式添加到 CustomEditorConfigurer 中的 PropertyEditorRegistrar 实例可以很容易地与 DataBinder 和 Spring MVC 控制器共享。此外,它避免了对自定义编辑器进行同步的需要:PropertyEditorRegistrar 预期为每次 Bean 创建尝试创建新的 PropertyEditor 实例。

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.

以下示例显示了如何创建自己的 PropertyEditorRegistrar 实现:

The following example shows how to create your own PropertyEditorRegistrar implementation:

  • Java

  • Kotlin

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...
	}
}
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...
	}
}

另请参阅 org.springframework.beans.support.ResourceEditorRegistrar 以获取 PropertyEditorRegistrar 实现的示例。请注意,在 registerCustomEditors(..) 方法的实现中,它是如何为每个属性编辑器创建新实例的。

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.

下一个示例显示了如何配置 CustomEditorConfigurer 并向其中注入我们自己的 CustomPropertyEditorRegistrar 实例:

The next example shows how to configure a CustomEditorConfigurer and inject an instance of our CustomPropertyEditorRegistrar into it:

<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"/>

最后(与本章的重点有点背离),对于那些使用 Spring’s MVC web framework 的人来说,将 PropertyEditorRegistrar 与数据绑定 Web 控制器结合使用会非常方便。以下示例在 @InitBinder 方法的实现中使用了 PropertyEditorRegistrar

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:

  • Java

  • Kotlin

@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
}

这种 PropertyEditor 注册样式可以生成简洁的代码(@InitBinder 方法的实现只有一行长),并允许将公共 PropertyEditor 注册代码封装在一个类中,然后在需要时在多个控制器中共享。

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.