此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

数据绑定

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

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

构造函数绑定

要使用构造函数绑定,请执行以下作:spring-doc.cadn.net.cn

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

  2. 设置targetType到目标类。spring-doc.cadn.net.cn

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

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

默认情况下,参数值是通过构造函数参数名称查找的。Spring MVC 和 WebFlux 支持通过@BindParam构造函数上的注释 参数或字段(如果存在)。如有必要,您还可以配置NameResolverDataBinder自定义要使用的参数名称。spring-doc.cadn.net.cn

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

构造函数绑定支持List,Map,数组参数从 单个字符串,例如逗号分隔的列表,或基于索引键(例如accounts[2].nameaccount[KEY].name.spring-doc.cadn.net.cn

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

属性绑定BeanWrapper

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

bean 包中一个非常重要的类是BeanWrapper接口及其 相应的实现 (BeanWrapperImpl).正如 javadoc 中引用的那样,BeanWrapper提供设置和获取属性值的功能(单独或在 bulk)、获取属性描述符和查询属性以确定它们是否 可读或可写。此外,BeanWrapper提供对嵌套属性的支持, 启用子属性上的属性设置到无限深度。这BeanWrapper还支持添加标准 JavaBeans 的功能PropertyChangeListenersVetoableChangeListeners,无需在目标类中支持代码。 最后但并非最不重要的一点是,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

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

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

accounts[KEY]spring-doc.cadn.net.cn

指示由KEY价值。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
}

以下代码片段显示了如何检索和作某些 实例化的属性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?

PropertyEditors

Spring 使用PropertyEditor实现ObjectString.它可以很方便 以与对象本身不同的方式表示属性。例如,一个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

将类表示为实际类的字符串,反之亦然。当 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.不 默认注册。必须根据需要使用适当的格式进行用户注册。spring-doc.cadn.net.cn

CustomNumberEditorspring-doc.cadn.net.cn

适用于任何的可定制属性编辑器Number子类,例如Integer,Long,FloatDouble.默认情况下,注册者BeanWrapperImpl但可以被 将其自定义实例注册为自定义编辑器。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因此InputStream属性可以直接设置为字符串。请注意,默认用法不会关闭 这InputStream给你的。默认情况下,注册者BeanWrapperImpl.spring-doc.cadn.net.cn

LocaleEditorspring-doc.cadn.net.cn

可以将字符串解析为Locale对象,反之亦然(字符串格式为[language]_[country]_[variant],与toString()方法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.Propertiesclass) 设置为Properties对象。默认情况下,已注册 由BeanWrapperImpl.spring-doc.cadn.net.cn

StringTrimmerEditorspring-doc.cadn.net.cn

修剪字符串的属性编辑器。可选地允许转换空字符串 变成一个null价值。默认情况下未注册 — 必须是用户注册的。spring-doc.cadn.net.cn

URLEditorspring-doc.cadn.net.cn

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

Spring 使用java.beans.PropertyEditorManager设置属性的搜索路径 可能需要的编辑。搜索路径还包括sun.bean.editors哪 包括PropertyEditor诸如Font,Color,以及大部分 原始类型。另请注意,标准 JavaBeans 基础架构 自动发现PropertyEditor类(无需注册它们 显式),如果它们与它们处理的类位于同一包中并且具有相同的 name 作为该类,并带有Editor附加。例如,可以有以下内容 类和包结构,这对于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())
		}

	}
}

习惯PropertyEditors

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

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

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

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

考虑以下示例,该示例定义了一个名为ExoticType和 另一个名为DependsOnExoticType,需要ExoticType设置为属性: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
}

当事情设置正确后,我们希望能够将类型属性分配为 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())
	}
}

最后,以下示例展示了如何使用CustomEditorConfigurer以注册新的PropertyEditor使用ApplicationContext,然后它将能够根据需要使用它: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

向 Spring 容器注册属性编辑器的另一种机制是 创建并使用PropertyEditorRegistrar.此界面在以下情况下特别有用 您需要在几种不同的情况下使用同一组属性编辑器。 您可以编写相应的注册商并在每种情况下重复使用它。PropertyEditorRegistrar实例与名为PropertyEditorRegistry,一个由 Spring 实现的接口BeanWrapper(和DataBinder).PropertyEditorRegistrar实例特别方便 当与CustomEditorConfigurer此处所述),它公开了一个属性 叫setPropertyEditorRegistrars(..).PropertyEditorRegistrar已添加实例 设置为CustomEditorConfigurer以这种方式可以轻松分享DataBinder和 Spring MVC 控制器。此外,它避免了自定义同步的需要 编辑:APropertyEditorRegistrar有望创造新鲜感PropertyEditor每个 Bean 创建尝试的实例。spring-doc.cadn.net.cn

以下示例演示如何创建自己的PropertyEditorRegistrar实现: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...
	}
}

另请参阅org.springframework.beans.support.ResourceEditorRegistrar举个例子PropertyEditorRegistrar实现。请注意,在实现registerCustomEditors(..)方法,它会创建每个属性编辑器的新实例。spring-doc.cadn.net.cn

下一个示例演示如何配置CustomEditorConfigurer并注入实例 我们的CustomPropertyEditorRegistrar进入其中: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"/>

最后(有点偏离本章的重点)给你们这些人 使用 Spring 的 MVC Web 框架,使用PropertyEditorRegistrar在 与数据绑定 Web 控制器结合使用可以非常方便。以下内容 示例使用PropertyEditorRegistrar在实施@InitBinder方法: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
}

这种风格的PropertyEditor注册可以导致简洁的代码(实现 的@InitBinder方法只有一行长),并且让PropertyEditor注册码封装在类中,然后在尽可能多的控制器之间共享 根据需要。spring-doc.cadn.net.cn