Spring BeanDefinition 到 Bean 实例创建
Spring 数据绑定(DataBinder)
Spring Web 参数绑定(WebDataBinder)
标准组件
Web 组件
DataBinder 核心属性
属性 | 说明 |
---|---|
target | 关联目标 Bean |
objectName | 目标 Bean 名称 |
bindingResult | 属性绑定结果 |
typeConverter | 类型转换器 |
conversionService | 类型转换服务 |
messageCodesResolver | 效验错误文案 Code 处理器 |
validators | 关联的 Bean Validator 实例集合 |
DataBinder 绑定方法
name = 小马哥
的键值对,同时 User 对象中存在 name 属性,当 bind 方法执行时,User 对象中的 name 属性值将被绑定为 小马哥
。DataBinder 元数据 - PropertyValues
特征 | 说明 |
---|---|
数据来源 | BeanDefinition,主要来源 XML 资源配置 BeanDefinition |
数据结构 | 由一个或者多个 PropertyValue 组成 |
成员结构 | PropertyValue 包含属性名称,以及属性值(包括原始值、类型转换后的值) |
常见实现 | MutablePropertyValues |
Web 扩展实现 | ServletConfigPropertyValues、ServletRequestParameterPropertyValues |
相关生命周期 | InstantiationAwareBeanPostProcessor#postProcessProperties |
通常来源于 XML 资源配置 BeanDefinition,因为 @Bean 或者其他编程方式,属性值可以直接使用,并不需要使用 PropertyValues 来进行转换。
通过 org.springframework.beans.factory.config.BeanDefinition#getPropertyValues 来获取。
示例
/**
* {@link DataBinder} 示例
*
* @see DataBinder
*/
public class DataBinderDemo {
public static void main(String[] args) {
User user = new User();
DataBinder dataBinder = new DataBinder(user, "user");
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("id", "1")
.add("name", "xwf")
// 添加一个不存在的属性值
// DataBinder 忽略未知属性
.add("otherName", "xwf")
// 嵌套属性
.add("company.name", "阿里");
dataBinder.bind(mpvs);
System.out.println(user);
}
}
新增 Company 类
public class Company {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
'}';
}
}
将属性增加到 User 类中,并新增 setter/getter 方法,重写 toString 方法。
执行结果:
User{id=1, name='xwf', age=null, configFileReource=null, city=null, cities=null, lifeCities=null, company=Company{name='阿里'}}
由结果可以得到两个结论
1.添加一个不存在的属性值,DataBinder 忽略未知属性
2.DataBinder 支持嵌套属性
参数名称 | 说明 |
---|---|
ignoreUnknownFields | 是否忽略未知字段,默认值:true |
ignoreInvalidFields | 是否忽略非法字段,默认值:false |
autoGrowNestedPaths | 是否自动增加嵌套路径,默认值:true |
allowedFields | 绑定字段白名单 |
disallowedFields | 绑定字段黑名单 |
requiredFields | 必须绑定字段 |
public class DataBinderDemo {
public static void main(String[] args) {
User user = new User();
DataBinder dataBinder = new DataBinder(user, "user");
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("id", "1")
.add("name", "xwf")
// 添加一个不存在的属性值
// DataBinder 忽略未知属性
.add("otherName", "xwf")
// 嵌套属性
.add("company.name", "阿里");
// 1.ignoreUnknownFields true(默认) -> false
dataBinder.setIgnoreUnknownFields(false);
dataBinder.bind(mpvs);
// 输出
System.out.println(user);
}
}
执行结果:
Exception in thread "main" org.springframework.beans.NotWritablePropertyException: Invalid property 'otherName' of bean class [com.huajie.thinking.in.spring.ioc.overview.domain.User]: Bean property 'otherName' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
结论:如果设置 ignoreUnknownFields 为false,绑定的字段不存在,抛出异常 NotWritablePropertyException
public class DataBinderDemo {
public static void main(String[] args) {
User user = new User();
DataBinder dataBinder = new DataBinder(user, "user");
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("id", "1")
.add("name", "xwf")
// 添加一个不存在的属性值
// DataBinder 忽略未知属性
.add("otherName", "xwf")
// 嵌套属性
.add("company.name", "阿里");
// 1.ignoreUnknownFields true(默认) -> false
// dataBinder.setIgnoreUnknownFields(false);
// 2.autoGrowNestedPaths true(默认) -> false
dataBinder.setAutoGrowNestedPaths(false);
dataBinder.bind(mpvs);
// 输出
System.out.println(user);
}
}
执行结果:
Exception in thread "main" org.springframework.beans.NullValueInNestedPathException: Invalid property 'company' of bean class [com.huajie.thinking.in.spring.ioc.overview.domain.User]: Value of nested property 'company' is null
设置 autoGrowNestedPaths 为 false 不支持嵌套属性。
ignoreInvalidFields 属性设置为 true,可以忽略 5.2.2 中的错误,但是相应出错的属性也不会设置
public class DataBinderDemo {
public static void main(String[] args) {
User user = new User();
DataBinder dataBinder = new DataBinder(user, "user");
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("id", "1")
.add("name", "xwf")
// 添加一个不存在的属性值
// DataBinder 忽略未知属性
.add("otherName", "xwf")
// 嵌套属性
.add("company.name", "阿里");
// 1.ignoreUnknownFields true(默认) -> false
// dataBinder.setIgnoreUnknownFields(false);
// 2.autoGrowNestedPaths true(默认) -> false
dataBinder.setAutoGrowNestedPaths(false);
// 3.ignoreInvalidFields false(默认) -> true
dataBinder.setIgnoreInvalidFields(true);
dataBinder.bind(mpvs);
// 输出
System.out.println(user);
}
}
执行结果:
User{id=1, name='xwf', age=null, configFileReource=null, city=null, cities=null, lifeCities=null, company=null}
dataBinder.getBindingResult();
获取,不会报错public class DataBinderDemo {
public static void main(String[] args) {
User user = new User();
DataBinder dataBinder = new DataBinder(user, "user");
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("id", "1")
.add("name", "xwf")
// 添加一个不存在的属性值
// DataBinder 忽略未知属性
.add("otherName", "xwf")
// 嵌套属性
.add("company.name", "阿里");
// 1.ignoreUnknownFields true(默认) -> false
// dataBinder.setIgnoreUnknownFields(false);
// 2.autoGrowNestedPaths true(默认) -> false
dataBinder.setAutoGrowNestedPaths(false);
// 3.ignoreInvalidFields false(默认) -> true
dataBinder.setIgnoreInvalidFields(true);
// 4.requiredFields 设置不能为空的字段
dataBinder.setRequiredFields("id","name","age");
dataBinder.bind(mpvs);
// 输出
System.out.println(user);
// 绑定结果(结果包含了错误文案信息,但是不会报错)
BindingResult bindingResult = dataBinder.getBindingResult();
System.out.println(bindingResult);
}
}
执行结果:
User{id=1, name='xwf', age=null, configFileReource=null, city=null, cities=null, lifeCities=null, company=null}
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'user' on field 'age': rejected value []; codes [required.user.age,required.age,required.java.lang.Integer,required]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.age,age]; arguments []; default message [age]]; default message [Field 'age' is required]
相关设置
dataBinder.setAllowedFields("id");//表示只允许 id 字段进行绑定
执行结果:
User{id=1, name='null', age=null, configFileReource=null, city=null, cities=null, lifeCities=null, company=null}
相关设置
dataBinder.setDisallowedFields("id");//表示不允许 id 字段进行绑定
执行结果:
User{id=null, name='xwf', age=null, configFileReource=null, city=null, cities=null, lifeCities=null, company=null}
JavaBeans 核心实现 - java.beans.BeanInfo
Spring 替代实现 - org.springframework.beans.BeanWrapper
BeanWrapper
BeanWrapper 接口源码中相关的方法
setAutoGrowCollectionLimit
org.springframework.beans.AbstractNestablePropertyAccessor 中
private int autoGrowCollectionLimit = Integer.MAX_VALUE;
默认实现中这个值是无限制的,表示支持无线的嵌套路径
getWrappedInstance 获取 Bean 的实例,在 IoC 场景下的 BeanWrapper 是和 Bean 关联的
getWrappedClass 获取 Bean 的 Class
getPropertyDescriptors 获取所有属性的描述信息
getPropertyDescriptor 获取单个属性的描述信息
标准 JavaBeans 是如何操作属性的?
API | 说明 |
---|---|
java.beans.Introspector | Java Beans 内省 API |
java.beans.BeanInfo | Java Bean 元信息 API |
java.beans.BeanDescriptor | Java Bean 信息描述符 |
java.beans.PropertyDescriptor | Java Bean 属性描述符 |
java.beans.MethodDescriptor | Java Bean 方法描述符 |
java.beans.EventSetDescriptor | Java Bean 事件集合描述符 |
简单示例
/**
* JavaBeans 示例
*/
public class JavaBeansDemo {
public static void main(String[] args) throws IntrospectionException {
// stopClass 排除类
BeanInfo beanInfo = Introspector.getBeanInfo(User.class,Object.class);
Stream.of(beanInfo.getPropertyDescriptors()).forEach(propertyDescriptor -> {
System.out.println(propertyDescriptor);
});
Stream.of(beanInfo.getMethodDescriptors()).forEach(System.out::println);
}
}
源码调用链路
org.springframework.validation.DataBinder#bind
由上面调用链路可以知道当调用 DataBinder#bind 方法时,默认会创建 BeanWrapper 对象,此对象和 BeanPropertyBindingResult 进行关联。
org.springframework.validation.DataBinder
org.springframework.validation.BeanPropertyBindingResult