对于最新的稳定版本,请使用 Spring Framework 6.2.4spring-doc.cadn.net.cn

数据绑定

数据绑定可用于将用户输入绑定到用户输入为映射的目标对象 将属性路径作为键,遵循 JavaBeans 约定DataBinder是支持该 API 的主类,它提供了两种方式来绑定 User 输入:spring-doc.cadn.net.cn

您可以同时应用构造函数和属性绑定,也可以只应用一个。spring-doc.cadn.net.cn

构造函数绑定

要使用构造函数绑定:spring-doc.cadn.net.cn

  1. 创建一个DataBindernull作为目标对象。spring-doc.cadn.net.cn

  2. 设置targetType添加到 Target 类。spring-doc.cadn.net.cn

  3. construct.spring-doc.cadn.net.cn

目标类应具有单个公共构造函数或单个非公共构造函数 with 参数。如果有多个构造函数,则默认构造函数(如果存在) 被使用。spring-doc.cadn.net.cn

默认情况下,构造函数参数名称用于查找参数值,但你可以 配置NameResolver.Spring MVC 和 WebFlux 都依赖于允许自定义名称 的值以通过@BindParam构造函数参数上的注释。spring-doc.cadn.net.cn

根据需要应用类型转换以转换用户输入。 如果 constructor 参数是一个对象,则它是在同一个 方式,而是通过嵌套的属性路径。这意味着构造函数绑定会同时创建 目标对象及其包含的任何对象。spring-doc.cadn.net.cn

绑定和转换错误反映在BindingResultDataBinder. 如果目标创建成功,则target设置为创建的实例 调用construct.spring-doc.cadn.net.cn

属性绑定BeanWrapper

org.springframework.beanspackage 遵循 JavaBeans 标准。 JavaBean 是一个具有默认无参数构造函数的类,它遵循 命名约定,其中(例如)名为bingoMadness愿意 具有 setter 方法setBingoMadness(..)和 getter 方法getBingoMadness().为 有关 JavaBeans 和规范的更多信息,请参阅 JavaBeansspring-doc.cadn.net.cn

beans 包中一个非常重要的类是BeanWrapperinterface 及其 相应的实现 (BeanWrapperImpl).引用自 javadoc,BeanWrapper提供设置和获取属性值(单独或在 bulk)、获取属性描述符和查询属性以确定它们是否为 readable 或 writable。此外,BeanWrapper提供对嵌套属性的支持, 将 子属性 的 属性设置为 无限深度 。这BeanWrapper还支持添加标准 JavaBeans 的功能PropertyChangeListenersVetoableChangeListeners,而无需 target 类中的支持代码。 最后但并非最不重要的一点是,BeanWrapper支持设置索引属性。 这BeanWrapper通常不直接由应用程序代码使用,但由DataBinderBeanFactory.spring-doc.cadn.net.cn

方式BeanWrapperworks 部分由它的名称表示:它将一个 bean 包装到 对该 Bean 执行作,例如设置和检索属性。spring-doc.cadn.net.cn

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

设置和获取属性是通过setPropertyValuegetPropertyValue的重载方法变体BeanWrapper.请参阅他们的 Javadoc 以获取 详。下表显示了这些约定的一些示例:spring-doc.cadn.net.cn

表 1.属性示例
表达 解释

namespring-doc.cadn.net.cn

指示属性name对应的getName()isName()setName(..)方法。spring-doc.cadn.net.cn

account.namespring-doc.cadn.net.cn

指示嵌套属性name的财产account对应于 (例如)该getAccount().setName()getAccount().getName()方法。spring-doc.cadn.net.cn

account[2]spring-doc.cadn.net.cn

指示 indexed 属性的第三个元素account.索引属性 可以是array,list或其他自然有序的集合。spring-doc.cadn.net.cn

account[COMPANYNAME]spring-doc.cadn.net.cn

指示由COMPANYNAME键的account Map财产。spring-doc.cadn.net.cn

(如果您不打算使用 这BeanWrapper径直。如果您只使用DataBinderBeanFactory及其默认实现,您应该跳到部分PropertyEditors.)spring-doc.cadn.net.cn

以下两个示例类使用BeanWrapper以获取并设置 性能:spring-doc.cadn.net.cn

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
}

以下代码片段显示了如何检索和作某些 instantiated 的属性Companys 和Employees:spring-doc.cadn.net.cn

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

Spring 使用PropertyEditor要在Object以及String.它可以很方便 以不同于对象本身的方式表示属性。例如,Date可以用人类可读的方式表示(因为String:'2007-14-09'),而 我们仍然可以将人类可读的形式转换回原始日期(或者,甚至 更好的是,将以人类可读形式输入的任何日期转换回Date对象)。这 可以通过注册java.beans.PropertyEditor.在BeanWrapper或 或者,在特定的 IoC 容器中(如上一章所述),会给出它 了解如何将属性转换为所需的类型。有关PropertyEditor的 javadocjava.beans来自 Oracle 的软件包.spring-doc.cadn.net.cn

在 Spring 中使用属性编辑的几个示例:spring-doc.cadn.net.cn

  • 在 bean 上设置属性是通过使用PropertyEditor实现。 当您使用String作为您声明的某个 bean 的属性值 在 XML 文件中,Spring(如果相应属性的 setter 具有Class参数)使用ClassEditor尝试将参数解析为Class对象。spring-doc.cadn.net.cn

  • 在 Spring 的 MVC 框架中解析 HTTP 请求参数是通过使用各种 之PropertyEditor可以在CommandController.spring-doc.cadn.net.cn

Spring 内置了许多PropertyEditor让生活更轻松的实施。 它们都位于org.springframework.beans.propertyeditors包。默认情况下,大多数 (但不是全部,如下表所示) 由BeanWrapperImpl.如果属性编辑器可以以某种方式进行配置,则可以 仍然注册你自己的变体来覆盖默认的变体。下表描述 各种PropertyEditorSpring 提供的实现:spring-doc.cadn.net.cn

表 2.内置PropertyEditor实现
解释

ByteArrayPropertyEditorspring-doc.cadn.net.cn

字节数组的编辑器。将字符串转换为相应的字节 交涉。默认注册者BeanWrapperImpl.spring-doc.cadn.net.cn

ClassEditorspring-doc.cadn.net.cn

将表示类的 String 解析为实际的类,反之亦然。当 class 时,会创建一个IllegalArgumentException被抛出。默认情况下,由BeanWrapperImpl.spring-doc.cadn.net.cn

CustomBooleanEditorspring-doc.cadn.net.cn

可定制的属性编辑器Boolean性能。默认情况下,由BeanWrapperImpl但可以通过将其自定义实例注册为 自定义编辑器。spring-doc.cadn.net.cn

CustomCollectionEditorspring-doc.cadn.net.cn

用于集合的属性编辑器, 转换任何源Collection到给定目标Collection类型。spring-doc.cadn.net.cn

CustomDateEditorspring-doc.cadn.net.cn

可定制的属性编辑器java.util.Date,支持自定义DateFormat.不 registered (默认)。必须根据需要使用适当的格式进行用户注册。spring-doc.cadn.net.cn

CustomNumberEditorspring-doc.cadn.net.cn

可定制的属性编辑器Number子类,例如Integer,Long,FloatDouble.默认情况下,由BeanWrapperImpl但可以被 将其自定义实例注册为 Custom Editor。spring-doc.cadn.net.cn

FileEditorspring-doc.cadn.net.cn

将字符串解析为java.io.File对象。默认情况下,由BeanWrapperImpl.spring-doc.cadn.net.cn

InputStreamEditorspring-doc.cadn.net.cn

单向属性编辑器,可以接受一个字符串并生成(通过 中间ResourceEditorResource) 和InputStream因此InputStreamproperties 可以直接设置为 strings。请注意,默认用法不会关闭 这InputStream给你的。默认情况下,由BeanWrapperImpl.spring-doc.cadn.net.cn

LocaleEditorspring-doc.cadn.net.cn

可以将字符串解析为Locale对象,反之亦然(字符串格式为[language]_[country]_[variant],与toString()method 的Locale).还接受空格作为分隔符,作为下划线的替代项。 默认情况下,由BeanWrapperImpl.spring-doc.cadn.net.cn

PatternEditorspring-doc.cadn.net.cn

可以将字符串解析为java.util.regex.Pattern对象,反之亦然。spring-doc.cadn.net.cn

PropertiesEditorspring-doc.cadn.net.cn

可以转换字符串(使用 javadoc 中定义的格式进行格式化java.util.Properties类)设置为Properties对象。默认情况下,已注册 由BeanWrapperImpl.spring-doc.cadn.net.cn

StringTrimmerEditorspring-doc.cadn.net.cn

修剪字符串的 Property editor。(可选)允许转换空字符串 转换为null价值。默认情况下未注册 — 必须由用户注册。spring-doc.cadn.net.cn

URLEditorspring-doc.cadn.net.cn

可以将 URL 的字符串表示形式解析为实际的URL对象。 默认情况下,由BeanWrapperImpl.spring-doc.cadn.net.cn

Spring 使用java.beans.PropertyEditorManager设置 Search path for 属性 可能需要的编辑器。搜索路径还包括sun.bean.editors哪 包括PropertyEditor类型的实现,例如Font,Color和大部分 原始类型。另请注意,标准的 JavaBeans 基础结构 自动发现PropertyEditor类(无需注册它们 显式地)如果它们与它们处理的类位于同一 package 中,并且具有相同的 name 作为该类,使用Editor附加。例如,可以有以下内容 class 和 package 结构,这对于SomethingEditor要成为的类 识别并用作PropertyEditorSomething-typed 属性。spring-doc.cadn.net.cn

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

请注意,您也可以使用标准的BeanInfoJavaBeans 机制 (在这里有一定程度的描述)。这 以下示例使用BeanInfo机制显式注册一个或多个PropertyEditor实例中具有关联类的属性:spring-doc.cadn.net.cn

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

以下 Java 源代码用于引用的SomethingBeanInfo类 合伙人 ACustomNumberEditor使用age属性的Something类:spring-doc.cadn.net.cn

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

	}
}

习惯PropertyEditor

当将 Bean 属性设置为字符串值时,Spring IoC 容器最终会使用 标准 JavaBeansPropertyEditor实现将这些字符串转换为 Complex 类型的 财产。Spring 预先注册了一些自定义PropertyEditor实现(例如,更改为 将表示为字符串的类名转换为Class对象)。此外 Java 的标准 JavaBeansPropertyEditorlookup 机制允许PropertyEditor,请适当命名,并将其放置在与类相同的包中 为此,它提供支持,以便可以自动找到它。spring-doc.cadn.net.cn

如有需要注册其他定制PropertyEditors,几种机制是 可用。最手动的方法,通常不方便或 推荐使用registerCustomEditor()方法ConfigurableBeanFactory接口,假设您有一个BeanFactory参考。 另一种(稍微方便一点)机制是使用特殊的咖啡豆工厂 后处理器称为CustomEditorConfigurer.虽然您可以使用 Bean Factory 后处理器 跟BeanFactoryimplementations、CustomEditorConfigurer具有 nested 属性设置,因此我们强烈建议您将它与ApplicationContext,您可以在其中以与任何其他 bean 类似的方式部署它,并且 可以自动检测和应用。spring-doc.cadn.net.cn

请注意,所有 bean 工厂和应用程序上下文都会自动使用一些 内置属性编辑器,通过使用BeanWrapper自 处理属性转换。标准属性编辑器,其中BeanWrapper寄存器在上一节中列出。 此外ApplicationContext还会覆盖或添加其他编辑器来处理 资源查找。spring-doc.cadn.net.cn

标准 JavaBeansPropertyEditor实例用于转换属性值 表示为属性的实际复杂类型的字符串。您可以使用CustomEditorConfigurer,一个 Bean Factory 后处理器,以方便地添加 支持其他PropertyEditor实例复制到ApplicationContext.spring-doc.cadn.net.cn

请考虑以下示例,该示例定义了一个名为ExoticType和 另一个名为DependsOnExoticType,它需要ExoticTypeset as a property:spring-doc.cadn.net.cn

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
}

当事情设置正确时,我们希望能够将 type 属性分配为 string,其中PropertyEditor转换为实际的ExoticType实例。以下 Bean 定义显示了如何设置此关系:spring-doc.cadn.net.cn

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

PropertyEditor实现可能类似于以下内容:spring-doc.cadn.net.cn

package example;

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()));
	}
}
package example

import java.beans.PropertyEditorSupport

// converts string representation to ExoticType object
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:spring-doc.cadn.net.cn

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

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-doc.cadn.net.cn

The following example shows how to create your own PropertyEditorRegistrar implementation:spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

The next example shows how to configure a CustomEditorConfigurer and inject an instance of our CustomPropertyEditorRegistrar into it:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn

@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.spring-doc.cadn.net.cn