此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
数据绑定
数据绑定对于将用户输入绑定到目标对象非常有用,其中用户输入是映射
使用属性路径作为键,遵循 JavaBeans 约定。DataBinder
是支持该作的主类,它提供了两种绑定用户的方式
输入:
可以同时应用构造函数和属性绑定,也可以仅应用一个。
构造函数绑定
要使用构造函数绑定,请执行以下作:
-
创建一个
DataBinder
跟null
作为目标对象。 -
设置
targetType
到目标类。 -
叫
construct
.
目标类应具有单个公共构造函数或单个非公共构造函数 带参数。如果有多个构造函数,则默认构造函数(如果存在) 被使用。
默认情况下,参数值是通过构造函数参数名称查找的。Spring MVC 和
WebFlux 支持通过@BindParam
构造函数上的注释
参数或字段(如果存在)。如有必要,您还可以配置NameResolver
上DataBinder
自定义要使用的参数名称。
根据需要应用类型转换来转换用户输入。 如果构造函数参数是对象,则在相同的 方式,但通过嵌套属性路径。这意味着构造函数绑定会同时创建 目标对象及其包含的任何对象。
构造函数绑定支持List
,Map
,数组参数从
单个字符串,例如逗号分隔的列表,或基于索引键(例如accounts[2].name
或account[KEY].name
.
绑定和转换错误反映在BindingResult
的DataBinder
.
如果成功创建了目标,则target
设置为创建的实例
调用后construct
.
属性绑定BeanWrapper
这org.springframework.beans
包遵循 JavaBeans 标准。
JavaBean 是一个具有默认无参数构造函数的类,它遵循
命名约定,其中(例如)名为bingoMadness
愿意
有一个 setter 方法setBingoMadness(..)
和一个 getter 方法getBingoMadness()
.为
有关 JavaBeans 和规范的更多信息,请参阅 Javabeans。
bean 包中一个非常重要的类是BeanWrapper
接口及其
相应的实现 (BeanWrapperImpl
).正如 javadoc 中引用的那样,BeanWrapper
提供设置和获取属性值的功能(单独或在
bulk)、获取属性描述符和查询属性以确定它们是否
可读或可写。此外,BeanWrapper
提供对嵌套属性的支持,
启用子属性上的属性设置到无限深度。这BeanWrapper
还支持添加标准 JavaBeans 的功能PropertyChangeListeners
和VetoableChangeListeners
,无需在目标类中支持代码。
最后但并非最不重要的一点是,BeanWrapper
支持设置索引属性。
这BeanWrapper
通常不直接由应用程序代码使用,而是由DataBinder
和BeanFactory
.
方式BeanWrapper
Works 的名称部分表明:它将 bean 包装成
对该 Bean 执行作,例如设置和检索属性。
设置和获取基本属性和嵌套属性
设置和获取属性是通过setPropertyValue
和getPropertyValue
重载方法变体BeanWrapper
.请参阅他们的 Javadoc
详。下表显示了这些约定的一些示例:
表达 | 解释 |
---|---|
|
指示属性 |
|
指示嵌套属性 |
|
指示索引属性的第三个元素 |
|
指示由 |
(如果您不打算与
这BeanWrapper
径直。如果您仅使用DataBinder
和BeanFactory
及其默认实现,您应该跳到部分PropertyEditors
.)
以下两个示例类使用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
}
以下代码片段显示了如何检索和作某些
实例化的属性Company
s 和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
看的 javadocjava.beans
来自 Oracle 的包.
在 Spring 中使用属性编辑的几个示例:
-
在 Bean 上设置属性是通过使用
PropertyEditor
实现。 当您使用String
作为您声明的某个 bean 的属性的值 在 XML 文件中,Spring(如果相应属性的 setter 具有Class
参数)使用ClassEditor
尝试将参数解析为Class
对象。 -
在 Spring 的 MVC 框架中解析 HTTP 请求参数是通过使用各种类型来完成的 之
PropertyEditor
可以在CommandController
.
Spring 内置了许多PropertyEditor
实施,让生活变得轻松。
它们都位于org.springframework.beans.propertyeditors
包。默认情况下,大多数(但不是全部,如下表所示)由BeanWrapperImpl
.如果属性编辑器可以以某种方式进行配置,您可以
仍然注册您自己的变体以覆盖默认变体。下表描述了
各种PropertyEditor
Spring 提供的实现:
类 | 解释 |
---|---|
|
字节数组的编辑器。将字符串转换为其相应的字节
交涉。默认注册者 |
|
将类表示为实际类的字符串,反之亦然。当
class 未找到,则 |
|
可自定义的属性编辑器 |
|
集合的属性编辑器,转换任何源 |
|
可自定义的属性编辑器 |
|
适用于任何的可定制属性编辑器 |
|
将字符串解析为 |
|
单向属性编辑器,可以接受字符串并生成(通过
中间 |
|
可以将字符串解析为 |
|
可以将字符串解析为 |
|
可以转换字符串(格式化为 javadoc 中定义的格式 |
|
修剪字符串的属性编辑器。可选地允许转换空字符串
变成一个 |
|
可以将 URL 的字符串表示形式解析为实际的 |
Spring 使用java.beans.PropertyEditorManager
设置属性的搜索路径
可能需要的编辑。搜索路径还包括sun.bean.editors
哪
包括PropertyEditor
诸如Font
,Color
,以及大部分
原始类型。另请注意,标准 JavaBeans 基础架构
自动发现PropertyEditor
类(无需注册它们
显式),如果它们与它们处理的类位于同一包中并且具有相同的
name 作为该类,并带有Editor
附加。例如,可以有以下内容
类和包结构,这对于SomethingEditor
类
识别并用作PropertyEditor
为Something
-typed 属性。
com chank pop Something SomethingEditor // the PropertyEditor for the Something class
请注意,您也可以使用BeanInfo
JavaBeans 机制也是如此
(此处在某种程度上进行了描述)。这
以下示例使用BeanInfo
显式注册一个或多个PropertyEditor
实例具有关联类的属性:
com chank pop Something SomethingBeanInfo // the BeanInfo for the Something class
以下 Java 源代码用于引用SomethingBeanInfo
类
员工 aCustomNumberEditor
使用age
属性的Something
类:
-
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
s
当将 bean 属性设置为字符串值时,Spring IoC 容器最终使用
标准 JavaBeansPropertyEditor
实现将这些字符串转换为
财产。Spring 预注册了许多自定义PropertyEditor
实现(例如,将
将表示为字符串的类名转换为Class
对象)。此外
Java 的标准 JavaBeansPropertyEditor
查找机制允许PropertyEditor
对于类,请适当命名并放置在与类相同的包中
它为其提供支持,以便可以自动找到它。
如果需要注册其他自定义PropertyEditors
,有几种机制
可用。最手动的方法,通常不方便或
recommended,就是使用registerCustomEditor()
方法ConfigurableBeanFactory
接口,假设你有一个BeanFactory
参考。
另一种(稍微方便一点)的机制是使用专门的豆厂
后处理器调用CustomEditorConfigurer
.虽然您可以使用 bean 工厂后处理器
跟BeanFactory
实现,则CustomEditorConfigurer
有一个
nested 属性设置,因此我们强烈建议您将其与ApplicationContext
,您可以在其中以与任何其他 Bean 类似的方式部署它,并且
它可以被自动检测和应用。
请注意,所有 bean 工厂和应用程序上下文都会自动使用许多
内置属性编辑器,通过使用BeanWrapper
自
处理属性转换。标准属性编辑器BeanWrapper
寄存器在上一节中列出。
此外ApplicationContext
也会覆盖或添加其他编辑器来处理
以适合特定应用程序上下文类型的方式进行资源查找。
标准 JavaBeansPropertyEditor
实例用于转换属性值
表示为属性的实际复杂类型的字符串。您可以使用CustomEditorConfigurer
,豆厂后处理器,方便添加
支持其他PropertyEditor
instances 到ApplicationContext
.
考虑以下示例,该示例定义了一个名为ExoticType
和
另一个名为DependsOnExoticType
,需要ExoticType
设置为属性:
-
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
}
当事情设置正确后,我们希望能够将类型属性分配为
string,其中PropertyEditor
转换为实际的ExoticType
实例。以下 bean 定义显示了如何设置此关系:
<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())
}
}
最后,以下示例展示了如何使用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>
PropertyEditorRegistrar
向 Spring 容器注册属性编辑器的另一种机制是
创建并使用PropertyEditorRegistrar
.此界面在以下情况下特别有用
您需要在几种不同的情况下使用同一组属性编辑器。
您可以编写相应的注册商并在每种情况下重复使用它。PropertyEditorRegistrar
实例与名为PropertyEditorRegistry
,一个由 Spring 实现的接口BeanWrapper
(和DataBinder
).PropertyEditorRegistrar
实例特别方便
当与CustomEditorConfigurer
(此处所述),它公开了一个属性
叫setPropertyEditorRegistrars(..)
.PropertyEditorRegistrar
已添加实例
设置为CustomEditorConfigurer
以这种方式可以轻松分享DataBinder
和
Spring MVC 控制器。此外,它避免了自定义同步的需要
编辑:APropertyEditorRegistrar
有望创造新鲜感PropertyEditor
每个 Bean 创建尝试的实例。
以下示例演示如何创建自己的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...
}
}
另请参阅org.springframework.beans.support.ResourceEditorRegistrar
举个例子PropertyEditorRegistrar
实现。请注意,在实现registerCustomEditors(..)
方法,它会创建每个属性编辑器的新实例。
下一个示例演示如何配置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"/>
最后(有点偏离本章的重点)给你们这些人
使用 Spring 的 MVC Web 框架,使用PropertyEditorRegistrar
在
与数据绑定 Web 控制器结合使用可以非常方便。以下内容
示例使用PropertyEditorRegistrar
在实施@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
注册可以导致简洁的代码(实现
的@InitBinder
方法只有一行长),并且让PropertyEditor
注册码封装在类中,然后在尽可能多的控制器之间共享
根据需要。