Java是一门面向对象的编程语言,Java程序在运行过程中,其数据对象都应该是JavaBean。将外部输入转化为JavaBean对象是通过反射技术实现的,BeanWrapper就是Spring对这一块的封装。本系列文章的源码分析是基于Spring 5.0版本进行的。
** 本文原创,转载请注明出处。**
BeanWrapper
包含属性读写和类型转换功能,本文重点分析属性读写相关的源码,类型转换相关的源码分析,见本系列的:Spring Core源码导读系列之TypeConverter。
什么是JavaBean
JavaBean
是一个Java类,该类包含:
- 一个无参构造函数
- 一些私有成员变量
- 成员变量的
getter
、setter
方法
如:
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; }
}
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; }
}
为了行文方便,这里需要明确两个定义:
- 类的属性:外界可以通过
getter
、setter
方法进行读写的成员变量,就是类的属性。 - 属性路径:类与类之间存在组合关系,如
Company
类通过managingDirector
属性组合了Employee
类。当我们需要表示组合类中的属性时,就需要引入属性路径,如:managingDirector.salary
。为了统一,单个属性也可以称为属性路径,如:name
。有时候为了区分两者,managingDirector.salary
这类型的属性路径也称为嵌套属性路径。
如何使用BeanWrapper
BeanWrapper
为我们操作JavaBean
对象的属性提供简单的接口:
Company company = new Company();
BeanWrapper beanWrapper = new BeanWrapperImpl(company);
// 相当于:company.setName("Some Company Inc.");
beanWrapper.setPropertyValue("name", "Some Company Inc.");
Employee employee = new Employee();
// 相当于:company.setManagingDirector(employee);
company.setPropertyValue("managingDirector", employee);
// 嵌套属性路径的使用示例,相当于:beanWrapper.getManagingDirector().setSalary(30.0F);
beanWrapper.setPropertyValue("managingDirector.salary",30.0F);
// 嵌套属性路径的使用示例,相当于:beanWrapper.getManagingDirector().getSalary();
Float salary = (Float) beanWrapper.getPropertyValue("managingDirector.salary");
可以看到,使用BeanWrapper
之后,只需要通过属性路径,就可以读写Bean
属性。属性路径有下面这几种写法:
|属性路径|含义|
|:|:|
|name
|无嵌套属性路径|
|account.name
|嵌套的属性路径|
|account[2]
|Array
、Collection
的元素类型属性路径|
|account[COMPANYNAME]
|Map
的元素类型属性路径|
BeanWrapper接口概览
TypeConverter
接口和PropertyEditorRegistry
接口是类型转换相关的接口,在Spring Core源码导读系列之TypeConverter一文中有详细分析,这里不再赘述。
PropertyAccessor接口
public interface PropertyAccessor {
/**
* 获取属性值
*/
Object getPropertyValue(String propertyName) throws BeansException;
/**
* 设置属性值,该方法有几个重载方法,这里只取一个分析
*/
void setPropertyValue(String propertyName, Object value) throws BeansException;
// 省略其他与分析无关重要方法
}
PropertyAccessor
接口是操作Bean
的一个核心接口,定义了读写Bean
属性的方法。
ConfigurablePropertyAccessor接口
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
/**
* 当执行setPropertyValue方法时,是否提取旧值,旧值可以通过事件监听获取到
*/
void setExtractOldValueForEditor(boolean extractOldValueForEditor);
boolean isExtractOldValueForEditor();
/**
* setPropertyValue过程中,如果是嵌套属性路径,如managingDirector.salary,
* 当managingDirector为null时,是否先为managingDirector赋默认值
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
boolean isAutoGrowNestedPaths();
// 省略其他与分析无关重要方法
}
ConfigurablePropertyAccessor
接口是一个组合接口,同时扩展了配置项。
BeanWrapper接口
public interface BeanWrapper extends ConfigurablePropertyAccessor {
/**
* 当属性类型为Collection或Array时,是否自动扩容
*/
void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
int getAutoGrowCollectionLimit();
/**
* 获取被包装对象
*/
Object getWrappedInstance();
// 省略其他与分析无关重要方法
}
BeanWrapper的实现
BeanWrapperImpl
类是BeanWrapper
接口的实现,这其中与类型转换相关的PropertyEditorRegistrySupport
类和TypeConverterSupport
类,在Spring Core源码导读系列之TypeConverter一文中有详细分析,这里不再赘述。
AbstractPropertyAccessor类
public abstract class AbstractPropertyAccessor extends TypeConverterSupport
implements ConfigurablePropertyAccessor {
// 实现ConfigurablePropertyAccessor接口定义的配置方法
private boolean extractOldValueForEditor = false;
private boolean autoGrowNestedPaths = false;
// 省略extractOldValueForEditor、autoGrowNestedPaths这两个属性的getter、setter方法
/**
* 实现PropertyAccessor接口定义的所有setPropertyValue重载方法,使所有重载最终调用该签名的方法
*/
public abstract void setPropertyValue(String propertyName, Object value) throws BeansException;
// 省略其他与分析无关重要方法
}
可以看到,AbstractPropertyAccessor
类完成了两个功能:
- 实现了
ConfigurablePropertyAccessor
接口定义的配置方法 - 适配了
PropertyAccessor
接口定义的所有setPropertyValue
重载方法,仅留出一个基础方法,便于子类实现
AbstractNestablePropertyAccessor类
阅读AbstractNestablePropertyAccessor
类源码之前,先来看一下其定义的两个内部类:PropertyTokenHolder
和PropertyHandler
。
- PropertyTokenHolder类
对于携带[]
的属性路径,如map[aa]
,解析为getMap().get("aa")
,因此这里应该拆分为两个属性:map
、aa
。但AbstractNestablePropertyAccessor
类将其组合为同一单元处理,并定义了PropertyTokenHolder
内部类表示这个组合属性。
/**
* 封装一个属性路径,目的是封装携带`[]`的属性路径,使其便于使用
* 例:属性路径map[aa]将会表示为 actualName="map", canonicalName = "map[aa]", keys=["aa"]
*/
protected static class PropertyTokenHolder {
public String actualName;
public String canonicalName;
public String[] keys;
public PropertyTokenHolder(String name) {
this.actualName = name;
this.canonicalName = name;
}
}
注意,PropertyTokenHolder
类不会用来表示aaa.bbb
这样的属性路径,AbstractNestablePropertyAccessor
类会将这个路径拆分为aaa
、bbb
两个属性分别处理。也就是说,组合属性仅存在于携带[]
的属性路径。
- PropertyHandler类
/**
* 该类封装属性读写的最基础逻辑
* getPropertyValue方法最终调用这个类的getValue方法完成属性值读取
* setPropertyValue方法最终调用这个类的setValue方法完成属性值设置
*/
protected abstract static class PropertyHandler {
public abstract Object getValue() throws Exception;
public abstract void setValue(Object value) throws Exception;
// 省略其他与分析无关重要方法
}
如注释,该类封装属性读写的最基础逻辑。其中最核心的getValue
、setValue
交给子类实现。
最后,回到AbstractNestablePropertyAccessor
类。
public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor {
/**
* 根据属性路径设置值
*/
public void setPropertyValue(String propertyName, Object value) throws BeansException {
// 1、解析属性路径,生成路径上倒数第二个属性的AbstractNestablePropertyAccessor
AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName);
// 2、将路径上最后一个属性包装为PropertyTokenHolder类型
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
// 3、设置路径上最后一个属性的值
nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
/**
* 设置路径上最后一个属性的值
*/
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
if (tokens.keys != null) {
// 设置携带[]的属性路径的值
processKeyedProperty(tokens, pv);
} else {
// 设置单个属性的值
processLocalProperty(tokens, pv);
}
}
// 省略其他与分析无关重要方法
}
上面分析了通过AbstractNestablePropertyAccessor
类设置属性值的整体脉络,大体如下:
- 对于嵌套的属性路径如
aa.bb.cc[k]
,先递归解析每一个属性,生成倒数第二个属性的AbstractNestablePropertyAccessor
实例,这里就是bb
。 - 将路径上最后一个属性包装为
PropertyTokenHolder
类型,这里就是actualName="cc"
,canonicalName="cc[k]"
,keys=["k"]
。 - 设置路径上最后一个属性的值,这里即是
bb.getCc().put("k",v)
。
接下来深入分析第1
、3
这两个大步骤:
- 生成倒数第二个属性的
AbstractNestablePropertyAccessor
实例
/**
* 递归生成属性路径中的AbstractNestablePropertyAccessor,返回倒数第二个属性的AbstractNestablePropertyAccessor
* 注意,携带[]的属性路径,算作一个属性,如cc[1]算作一个属性,bb.cc[1]算作bb和cc[1]两个属性
*/
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
if (pos > -1) {
// 属性路径的第一个属性
String nestedProperty = propertyPath.substring(0, pos);
// 属性路径截去第一个属性后的子路径
String nestedPath = propertyPath.substring(pos + 1);
// 创建第一个属性的AbstractNestablePropertyAccessor
AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);
// 递归创建嵌套属性的AbstractNestablePropertyAccessor
return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
} else {
return this;
}
}
/**
* 创建读写属性的AbstractNestablePropertyAccessor实例
*/
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
// 如果属性值为空,又配置了自动赋默认值,则设置默认值,否则报异常
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
String canonicalName = tokens.canonicalName;
Object value = getPropertyValue(tokens);
if (value == null || (value instanceof Optional && !((Optional) value).isPresent())) {
if (isAutoGrowNestedPaths()) {
value = setDefaultValue(tokens);
} else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
}
}
// 新建一个AbstractNestablePropertyAccessor
AbstractNestablePropertyAccessor nestedPa =
newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
return nestedPa;
}
/**
* 创建AbstractNestablePropertyAccessor实例的方法是一个抽象方法,交由子类实现
*/
protected abstract AbstractNestablePropertyAccessor newNestedPropertyAccessor(Object object, String nestedPath);
由此可见,AbstractNestablePropertyAccessor
类实现了嵌套属性路径的全部逻辑。
- 设置属性值
/**
* 设置属性的值
*/
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
if (tokens.keys != null) {
// 设置携带[]的属性的值
processKeyedProperty(tokens, pv);
} else {
// 设置单个属性的值
processLocalProperty(tokens, pv);
}
}
可以看到,对于携带[]
类型的属性和不携带[]
类型的属性设置值,处理方式不一样。先分析不携带[]
类型的属性设置值:
/**
* 设置不携带[]的属性的属性值
*/
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
// 1、获取读写值处理类
PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
// 2、转换值类型
Object valueToApply = convertForProperty(
tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
// 3、设置值
ph.setValue(valueToApply);
}
/**
* 获取读写值处理类,交由子类实现
*/
protected abstract PropertyHandler getLocalPropertyHandler(String propertyName);
可以看到,对于不携带[]
类型的属性设置值,逻辑清晰明了,再来看看携带[]
类型的属性设置值:
/**
* 设置携带[]的属性的属性值
*/
private void processKeyedProperty(PropertyTokenHolder tokens, PropertyValue pv) {
// 1、获取actualName部分的属性值,用于接下来的[]设置值
Object propValue = getPropertyHoldingValue(tokens);
if (propValue.getClass().isArray()) {
// 2、类型转换
Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
requiredType, ph.nested(tokens.keys.length));
// 3、根据配置及需要,数组扩容
int length = Array.getLength(propValue);
if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
// 扩容过程略
}
// 4、将值设置到数组中
Array.set(propValue, arrayIndex, convertedValue);
} else if (propValue instanceof List) {
// 类似Array,省略
} else if (propValue instanceof Map) {
// 类似Array,省略
} else {
// 抛异常,省略
}
}
/**
* 获取携带[]的属性actualName部分的值,用于接下来的[]设置值
*/
private Object getPropertyHoldingValue(PropertyTokenHolder tokens) {
// 1、创建actualName部分的PropertyTokenHolder对象
PropertyTokenHolder getterTokens = new PropertyTokenHolder(tokens.actualName);
// 2、获取actualName部分的值
Object propValue = getPropertyValue(getterTokens);
if (propValue == null) {
// 3、当值为null且配置了自动赋默认值时,赋默认值
if (isAutoGrowNestedPaths()) {
propValue = setDefaultValue(getterTokens);
}
else {
// 抛异常,省略
}
}
return propValue;
}
对于携带[]
类型的属性设置值,先获取非[]
部分的属性值,再处理[]
部分的属性设置值,逻辑同样简单明了。
由此可见,AbstractNestablePropertyAccessor
类实现了读写[]
类型属性的逻辑,子类仅仅需要实现读写简单属性的接口的方法。
BeanWrapperImpl类
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
/**
* 读写属性值的策略类,通过反射实现属性读写
*/
private class BeanPropertyHandler extends PropertyHandler {
// 读取属性值
public Object getValue() throws Exception {
Method readMethod = ...
ReflectionUtils.makeAccessible(readMethod);
return readMethod.invoke(getWrappedInstance(), (Object[]) null);
}
// 设置属性值
public void setValue(Object value) throws Exception {
Method writeMethod = ...
ReflectionUtils.makeAccessible(writeMethod);
writeMethod.invoke(getWrappedInstance(), value);
}
}
// 省略其他与分析无关重要方法
}
如上,BeanWrapperImpl
类主要实现了一个读写属性值的策略类。
总结
最后,以一张序列图总结通过BeanWrapperImpl
类设置一个属性值需要的大体步骤,从图中可以清楚地看到各个相关类的职责。