数据绑定可用于将用户输入绑定到用户输入为映射的目标对象
将属性路径作为键,遵循 JavaBeans 约定。 是支持该 API 的主类,它提供了两种方式来绑定 User
输入:DataBinder
您可以同时应用构造函数和属性绑定,也可以只应用一个。
构造函数绑定
要使用构造函数绑定:
- 
创建一个 with 作为目标对象。
DataBindernull - 
设置为 target 类。
targetType - 
叫。
construct 
目标类应具有单个公共构造函数或单个非公共构造函数 with 参数。如果有多个构造函数,则默认构造函数(如果存在) 被使用。
默认情况下,构造函数参数名称用于查找参数值,但你可以
配置 .Spring MVC 和 WebFlux 都依赖于允许自定义名称
的值。NameResolver@BindParam
根据需要应用类型转换以转换用户输入。 如果 constructor 参数是一个对象,则它是在同一个 方式,而是通过嵌套的属性路径。这意味着构造函数绑定会同时创建 目标对象及其包含的任何对象。
绑定和转换错误反映在 的 中。
如果目标创建成功,则将其设置为创建的实例
在调用 .BindingResultDataBindertargetconstruct
属性绑定BeanWrapper
该软件包遵循 JavaBeans 标准。
JavaBean 是一个具有默认无参数构造函数的类,它遵循
命名约定,其中(例如)名为 would 的属性
具有 setter 方法和 getter 方法。为
有关 JavaBeans 和规范的更多信息,请参阅 JavaBeans。org.springframework.beansbingoMadnesssetBingoMadness(..)getBingoMadness()
bean 包中一个非常重要的类是接口及其
相应的实现 ()。正如 javadoc 中引用的那样,提供了设置和获取属性值(单独或在
bulk)、获取属性描述符和查询属性以确定它们是否为
readable 或 writable。此外,它还提供对嵌套属性
将 子属性 的 属性设置为 无限深度 。还支持添加标准 JavaBeans 和 的功能,而无需在目标类中支持代码。
最后但并非最不重要的一点是,它支持设置索引属性。
通常不由应用程序代码直接使用,但由 和 使用 。BeanWrapperBeanWrapperImplBeanWrapperBeanWrapperBeanWrapperPropertyChangeListenersVetoableChangeListenersBeanWrapperBeanWrapperDataBinderBeanFactory
它的工作方式部分由它的名称表示:它将一个 bean 包装到
对该 Bean 执行操作,例如设置和检索属性。BeanWrapper
设置和获取基本属性和嵌套属性
设置和获取属性是通过 的 和 重载方法变体完成的。请参阅他们的 Javadoc 以获取
详。下表显示了这些约定的一些示例:setPropertyValuegetPropertyValueBeanWrapper
| 表达 | 解释 | 
|---|---|
  | 
指示与 or 和 方法对应的属性。  | 
  | 
指示对应于
 (例如)OR 方法。  | 
  | 
指示 indexed property 的第三个元素 。索引属性
 可以是 , , 或其他自然有序的集合。  | 
  | 
指示由属性的键编制索引的映射条目的值。  | 
(如果您不打算使用
的 直接 。如果您只使用 和 以及它们的默认实现,您应该跳到 PropertyEditor 部分。BeanWrapperDataBinderBeanFactory
以下两个示例类使用 to get 和 set
性能:BeanWrapper
- 
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
}
以下代码片段显示了如何检索和操作某些
实例化的 S 和 S 的特性:CompanyEmployee
- 
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?
| 表达 | 解释 | 
|---|---|
  | 
指示与 or 和 方法对应的属性。  | 
  | 
指示对应于
 (例如)OR 方法。  | 
  | 
指示 indexed property 的第三个元素 。索引属性
 可以是 , , 或其他自然有序的集合。  | 
  | 
指示由属性的键编制索引的映射条目的值。  | 
PropertyEditor的
Spring 使用 a 的概念来实现 an 和 a 之间的转换。它可以很方便
以不同于对象本身的方式表示属性。例如,a 可以用人类可读的方式表示(如 :),而
我们仍然可以将人类可读的形式转换回原始日期(或者,甚至
更好的是,将以人类可读形式输入的任何日期转换回对象)。这
可以通过注册 类型的自定义编辑器来实现行为。在 or 上注册自定义编辑器,
或者,在特定的 IoC 容器中(如上一章所述),会给出
了解如何将属性转换为所需的类型。有关 的更多信息,请参阅 Oracle 的 java.beans 软件包的 javadoc。PropertyEditorObjectStringDateString'2007-14-09'Datejava.beans.PropertyEditorBeanWrapperPropertyEditor
在 Spring 中使用属性编辑的几个示例:
- 
在 bean 上设置属性是通过使用实现来完成的。 当你将声明的某个 bean 的值用作 在 XML 文件中,Spring (如果相应属性的 setter 具有参数)用于尝试将参数解析为对象。
PropertyEditorStringClassClassEditorClass - 
在 Spring 的 MVC 框架中解析 HTTP 请求参数是通过使用各种来完成的 的实现,您可以在 .
PropertyEditorCommandController 
Spring 具有许多内置实现,使生活变得轻松。
它们都位于包中。默认情况下,大多数(但不是全部,如下表所示)由 注册。如果属性编辑器可以以某种方式进行配置,则可以
仍然注册你自己的变体来覆盖默认的变体。下表描述
Spring 提供的各种实现:PropertyEditororg.springframework.beans.propertyeditorsBeanWrapperImplPropertyEditor
| 类 | 解释 | 
|---|---|
  | 
字节数组的编辑器。将字符串转换为相应的字节
 交涉。默认情况下由 注册。  | 
  | 
将表示类的 String 解析为实际的类,反之亦然。当
 class 的 git 中,则会引发 an。默认情况下,由 注册。  | 
  | 
用于属性的可自定义属性编辑器。默认情况下,由 registered by 但可以通过将其自定义实例注册为
 自定义编辑器。  | 
  | 
集合的 Property 编辑器,将任何源转换为给定的目标类型。  | 
  | 
的可自定义属性编辑器 ,支持自定义 .不
 registered (默认)。必须根据需要使用适当的格式进行用户注册。  | 
  | 
任何子类的可自定义属性编辑器,例如 、 、 或 。默认情况下,由 registered by 但可以被
 将其自定义实例注册为 Custom Editor。  | 
  | 
将字符串解析为对象。默认情况下,由 注册。  | 
  | 
单向属性编辑器,可以接受一个字符串并生成(通过
 intermediate 和 ) an,以便可以直接将属性设置为字符串。请注意,默认用法不会关闭
 的为你。默认情况下,由 注册。  | 
  | 
可以将字符串解析为对象,反之亦然(字符串格式为 ,与 的方法相同)。还接受空格作为分隔符,作为下划线的替代项。
 默认情况下,由 注册。  | 
  | 
可以将字符串解析为对象,反之亦然。  | 
  | 
可以将字符串(使用类的 javadoc 中定义的格式进行格式化)转换为对象。默认情况下,已注册
 由。  | 
  | 
修剪字符串的 Property editor。(可选)允许转换空字符串
 转换为值。默认情况下未注册 — 必须由用户注册。  | 
  | 
可以将 URL 的字符串表示形式解析为实际对象。
 默认情况下,由 注册。  | 
Spring 使用 来设置 属性的搜索路径
可能需要的编辑器。搜索路径还包括 ,其中
包括类型(如 、 和大多数
原始类型。另请注意,标准的 JavaBeans 基础结构
自动发现类(无需注册它们
显式地)如果它们与它们处理的类位于同一 package 中,并且具有相同的
name 作为该类,并附加例如,可以有以下内容
class 和 package 结构,这足以使 class 为
识别并用作 for -typed 属性。java.beans.PropertyEditorManagersun.bean.editorsPropertyEditorFontColorPropertyEditorEditorSomethingEditorPropertyEditorSomething
com
  chank
    pop
      Something
      SomethingEditor // the PropertyEditor for the Something class
请注意,您也可以在此处使用标准的 JavaBeans 机制
(在这里有一定程度的描述)。这
以下示例使用该机制将一个或多个实例显式注册到 Associated Class 的属性中:BeanInfoBeanInfoPropertyEditor
com
  chank
    pop
      Something
      SomethingBeanInfo // the BeanInfo for the Something class
引用类的以下 Java 源代码
将 a 与类的属性相关联:SomethingBeanInfoCustomNumberEditorageSomething
- 
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())
		}
	}
}
自定义PropertyEditor
当将 Bean 属性设置为字符串值时,Spring IoC 容器最终会使用
标准 JavaBeans 实现将这些字符串转换为
财产。Spring 预先注册了许多自定义实现(例如,将
将表示为 String 的类名转换为 Object)。此外
Java 的标准 JavaBeans 查找机制允许对类进行适当命名,并将其与类放在同一个包中
为此,它提供支持,以便可以自动找到它。PropertyEditorPropertyEditorClassPropertyEditorPropertyEditor
如果需要注册其他自定义,有几种机制是
可用。最手动的方法,通常不方便或
推荐,就是使用接口的方法,假设你有个参考。
另一种(稍微方便一点)机制是使用特殊的咖啡豆工厂
名为 .虽然您可以使用 Bean Factory 后处理器
对于实现,它有一个
nested 属性设置,因此我们强烈建议您将它与 一起使用,在那里您可以以与任何其他 bean 类似的方式部署它,并且
可以自动检测和应用。PropertyEditorsregisterCustomEditor()ConfigurableBeanFactoryBeanFactoryCustomEditorConfigurerBeanFactoryCustomEditorConfigurerApplicationContext
请注意,所有 bean 工厂和应用程序上下文都会自动使用一些
内置属性编辑器,通过使用 TO
处理属性转换。寄存器的标准属性编辑器在上一节中列出。
此外,s 还会覆盖或添加其他编辑器来处理
资源查找。BeanWrapperBeanWrapperApplicationContext
标准 JavaBeans 实例用于转换属性值
表示为属性的实际复杂类型的字符串。您可以使用 ,一个 bean factory 后处理器,方便地添加
支持将其他实例复制到 .PropertyEditorCustomEditorConfigurerPropertyEditorApplicationContext
请考虑以下示例,该示例定义了一个名为 和
另一个名为 的类,需要将其设置为属性:ExoticTypeDependsOnExoticTypeExoticType
- 
Java
 - 
Kotlin
 
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,将其转换为实际实例。以下 Bean 定义显示了如何设置此关系:PropertyEditorExoticType
<bean id="sample" class="example.DependsOnExoticType">
	<property name="type" value="aNameForExoticType"/>
</bean>
实现可能类似于以下内容:PropertyEditor
- 
Java
 - 
Kotlin
 
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())
	}
}
最后,以下示例显示了如何使用 向 注册 new,然后 将能够根据需要使用它:CustomEditorConfigurerPropertyEditorApplicationContext
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
		</map>
	</property>
</bean>
PropertyEditorRegistrar
向 Spring 容器注册属性编辑器的另一种机制是
创建并使用 .此接口在以下情况下特别有用
您需要在几种不同情况下使用同一组属性编辑器。
您可以编写相应的注册商并在每种情况下重复使用它。 实例与一个名为 的接口一起工作,该接口由 Spring(和 )实现。 实例特别方便
当与 (在此处描述) 结合使用时,它会公开一个属性
叫。 已添加的实例
以这种方式可以轻松共享给 和
Spring MVC 控制器。此外,它还避免了在自定义时进行同步的需要
editors:A 应为每次 bean 创建尝试创建新的实例。PropertyEditorRegistrarPropertyEditorRegistrarPropertyEditorRegistryBeanWrapperDataBinderPropertyEditorRegistrarCustomEditorConfigurersetPropertyEditorRegistrars(..)PropertyEditorRegistrarCustomEditorConfigurerDataBinderPropertyEditorRegistrarPropertyEditor
以下示例显示如何创建自己的实现:PropertyEditorRegistrar
- 
Java
 - 
Kotlin
 
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...
	}
}
另请参阅 for an example implementation.请注意,在该方法的实现中,它是如何为每个属性编辑器创建新实例的。org.springframework.beans.support.ResourceEditorRegistrarPropertyEditorRegistrarregisterCustomEditors(..)
下一个示例演示如何配置实例并注入实例
我们进入它:CustomEditorConfigurerCustomPropertyEditorRegistrar
<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 框架,在
与数据绑定 Web 控制器结合使用可以非常方便。以下内容
example 在方法的实现中使用 a:PropertyEditorRegistrarPropertyEditorRegistrar@InitBinder
- 
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@InitBinderPropertyEditor
| 类 | 解释 | 
|---|---|
  | 
字节数组的编辑器。将字符串转换为相应的字节
 交涉。默认情况下由 注册。  | 
  | 
将表示类的 String 解析为实际的类,反之亦然。当
 class 的 git 中,则会引发 an。默认情况下,由 注册。  | 
  | 
用于属性的可自定义属性编辑器。默认情况下,由 registered by 但可以通过将其自定义实例注册为
 自定义编辑器。  | 
  | 
集合的 Property 编辑器,将任何源转换为给定的目标类型。  | 
  | 
的可自定义属性编辑器 ,支持自定义 .不
 registered (默认)。必须根据需要使用适当的格式进行用户注册。  | 
  | 
任何子类的可自定义属性编辑器,例如 、 、 或 。默认情况下,由 registered by 但可以被
 将其自定义实例注册为 Custom Editor。  | 
  | 
将字符串解析为对象。默认情况下,由 注册。  | 
  | 
单向属性编辑器,可以接受一个字符串并生成(通过
 intermediate 和 ) an,以便可以直接将属性设置为字符串。请注意,默认用法不会关闭
 的为你。默认情况下,由 注册。  | 
  | 
可以将字符串解析为对象,反之亦然(字符串格式为 ,与 的方法相同)。还接受空格作为分隔符,作为下划线的替代项。
 默认情况下,由 注册。  | 
  | 
可以将字符串解析为对象,反之亦然。  | 
  | 
可以将字符串(使用类的 javadoc 中定义的格式进行格式化)转换为对象。默认情况下,已注册
 由。  | 
  | 
修剪字符串的 Property editor。(可选)允许转换空字符串
 转换为值。默认情况下未注册 — 必须由用户注册。  | 
  | 
可以将 URL 的字符串表示形式解析为实际对象。
 默认情况下,由 注册。  |