1、BeanWrapper简介
BeanWrapper接口,作为spring内部的一个核心接口,正如其名,它是bean的包裹类,即在内部中将会保存该bean的实例,提供其它一些扩展功能。同时,BeanWrapper接口还继承了PropertyAccessor, propertyEditorRegistry, TypeConverter、ConfigurablePropertyAccessor接口,所以它还提供了访问bean的属性值、属性编辑器注册、类型转换等功能。
下面我们一起回顾一下bean的实例化过程,看一下spring是怎么使用BeanWrapper。
bean的实例化过程
(1)ResourceLoader加载配置信息 (2)BeanDefinitionReader读取并解析<bean>标签,并将<bean>标签的属性转换为BeanDefinition对应的属性,并注册到BeanDefinitionRegistry注册表中。 (3)容器扫描BeanDefinitionRegistry注册表,通过反射机制获取BeanFactoryPostProcessor类型的工厂后处理器,并用这个工厂后处理器对BeanDefinition进行加工。 (4)根据处理过的BeanDefinition,实例化bean。然后BeanWrapper结合BeanDefinitionRegistry和PropertyEditorRegistry对Bean的属性赋值。 |
在上面的bean实例化过程中,BeanWrapper取出XML中定义的值,然后通过属性编辑器或类型转换器把xml中的字符串值转换成bean中属性对应的类型,最后以内省的方式设置到bean的属性。
其实,在BeanWrapper接口中,最核心的功能就是读取和设置bean的属性,它是通过java内省的方式完成bean属性的访问的。为了能够感性认识BeanWrapper接口,下面将通过一个例子看一下怎么使用BeanWrapper访问bean的属性。
2、BeanWrapper的使用
前面介绍过,BeanWrapper接口继承了PropertyAccessor接口,所以它具备了访问bean的属性的能力。我们看一下PropertyAccessor接口的一些方法:
public interface PropertyAccessor { String NESTED_PROPERTY_SEPARATOR = "."; char NESTED_PROPERTY_SEPARATOR_CHAR = '.'; String PROPERTY_KEY_PREFIX = "["; char PROPERTY_KEY_PREFIX_CHAR = '['; String PROPERTY_KEY_SUFFIX = "]"; char PROPERTY_KEY_SUFFIX_CHAR = ']'; boolean isReadableProperty(String propertyName); boolean isWritableProperty(String propertyName); Class<?> getPropertyType(String propertyName) throws BeansException; TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException; Object getPropertyValue(String propertyName) throws BeansException; void setPropertyValue(String propertyName, Object value) throws BeansException; void setPropertyValue(PropertyValue pv) throws BeansException; void setPropertyValues(Map<?, ?> map) throws BeansException; void setPropertyValues(PropertyValues pvs) throws BeansException; void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException; void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException; }
PropertyAccessor接口提供了访问bean属性的方法,还定义了访问嵌套属性的访问表达式的切割符,重点看一下下面两个方法:
Object getPropertyValue(String propertyName) throws BeansException; void setPropertyValue(String propertyName, Object value) throws BeansException;
getPropertyValue和setPropertyValue是分别用于获取和设置bean的属性值的。这里的propertyName支持表达式:
表达式 | 说明 |
name | 指向属性name,与getName() 或 isName() 和 setName()相对应。 |
account.name | 指向属性account的嵌套属性name,与之对应的是getAccount().setName()和getAccount().getName() |
account[2] | 指向索引属性account的第三个元素,索引属性可能是一个数组(array),列表(list)或其它天然有序的容器。 |
account[COMPANYNAME] | 指向一个Map实体account中以COMPANYNAME作为键值(key)所对应的值 |
举个栗子。我们需要使用BeanWrapper访问它们的属性。
public class Car { private String name; private Wheel[] wheels; private Driver driver; public String getName() { return name; } public void setName(String name) { this.name = name; } public Wheel[] getWheels() { return wheels; } public void setWheels(Wheel[] wheels) { this.wheels = wheels; } public Driver getDriver() { return driver; } public void setDriver(Driver driver) { this.driver = driver; } @Override public String toString() { return "Car{" + "name='" + name + '\'' + ", wheels=" + Arrays.toString(wheels) + ", driver=" + driver + '}'; } }
public class Driver { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Driver{" + "age=" + age + '}'; } }
public class Wheel { private String position; public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } @Override public String toString() { return "Wheel{" + "position='" + position + '\'' + '}'; } }
public static void main(String[] args) { //左边轮子的BeanWrapper Wheel leftWheel = new Wheel(); BeanWrapper beanWrapperOfLeftWheel = PropertyAccessorFactory.forBeanPropertyAccess(leftWheel); PropertyValue leftPosition = new PropertyValue("position", "左边"); beanWrapperOfLeftWheel.setPropertyValue(leftPosition); System.out.println(beanWrapperOfLeftWheel.getWrappedInstance()); //左边轮子的BeanWrapper Wheel rightWheel = new Wheel(); BeanWrapper beanWrapperOfRightWheel = PropertyAccessorFactory.forBeanPropertyAccess(rightWheel); PropertyValue rightPosition = new PropertyValue("position", "右边"); beanWrapperOfRightWheel.setPropertyValue(rightPosition); System.out.println(beanWrapperOfRightWheel.getWrappedInstance()); // 驾驶员 Driver driver = new Driver(); BeanWrapper beanWrapperOfDriver = PropertyAccessorFactory.forBeanPropertyAccess(driver); PropertyValue age = new PropertyValue("age", 20); beanWrapperOfDriver.setPropertyValue(age); System.out.println(beanWrapperOfDriver.getWrappedInstance()); // 车子 Car car = new Car(); BeanWrapper beanWrapperOfCar = PropertyAccessorFactory.forBeanPropertyAccess(car); beanWrapperOfCar.setPropertyValue("name", "奔驰"); // 车名 Wheel[] wheels = {leftWheel, rightWheel}; //轮子数组 beanWrapperOfCar.setPropertyValue("wheels", wheels); //轮子 beanWrapperOfCar.setPropertyValue("driver", driver); //驾驶员 System.out.println(beanWrapperOfCar.getWrappedInstance()); // 获取驾驶员的年龄 int retrievedAge = (Integer) beanWrapperOfCar.getPropertyValue("driver.age"); System.out.println("driver age : " + retrievedAge); // 通过表达式间接设置car的wheel的width beanWrapperOfCar.setPropertyValue("wheels[0].position", "修改过的左边"); System.out.println(beanWrapperOfCar.getWrappedInstance()); }
上面的代码已经很清楚地演示了如何使用BeanWrapper设置和获取bean的属性,有了初步的认识之后,接下来将会解析该源码。
3、解析BeanWrapper源码
BeanWrapper的实现类是BeanWrapperImpl,它包装了bean对象,缓存了bean的内省结果,并可以访问bean的属性、设置bean的属性值。除此之外,BeanWrapperImpl类提供了许多默认属性编辑器,支持多种不同类型的类型转换,例如,可以将数组、集合类型的属性转换成指定特殊类型的数组或集合。用户也可以注册自定义的属性编辑器在BeanWrapperImpl中。
另外,BeanWrapperImpl有一个cachedIntrospectionResults成员变量,它保存了被包装bean的内省分析结果。cachedIntrospectionResults有两个成员变量,一个是beanInfo,它是被包裹类的BeanInfo;另一个是propertyDescriptorCache,它缓存了被包裹类的所有属性的属性描述器PropertyDescriptor。

我们看一下BeanWrapperImpl的结构类图。

3.1 构造方法
BeanWrapperImpl重载了很多种构造方法,我们看一下通过传入bean实例作为参数的构造方法。
public BeanWrapperImpl(Object object) { super(object); }
调用父类AbstractNestablePropertyAccessor的构造方法。
protected AbstractNestablePropertyAccessor(Object object) { registerDefaultEditors(); // 标识可以使用默认的属性编辑器 setWrappedInstance(object); }
public void setWrappedInstance(Object object) { setWrappedInstance(object, "", null); }
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { // bean设置为BeanWrapperImpl的内部变量 if (object.getClass() == javaUtilOptionalClass) { this.wrappedObject = OptionalUnwrapper.unwrap(object); } else { this.wrappedObject = object; } this.nestedPath = (nestedPath != null ? nestedPath : ""); this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.wrappedObject); this.nestedPropertyAccessors = null; // 新建类型转换器的委托类,这里BeanWrapperImpl的实例为propertyEditorRegistry,bean为targetObject this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); }
很明显,构造方法中做了两件重要的事,一个是把bean设为内部变量,另一个是实例化了一个类型转换器的委托类,由于BeanWrapperImpl同时继承了PropertyEditorRegistrySupport,所以它作为TypeConverterDelegate的属性编辑器注册中心的帮助类存在于TypeConverterDelegate中。
3.2 设置属性
BeanWrapperImpl支持多种设置bean属性的方法。

先看一下 void setPropertyValue(String propertyName, Object value)
public void setPropertyValue(String propertyName, Object value) throws BeansException { AbstractNestablePropertyAccessor nestedPa; try { //根据属性名获取BeanWrapImpl对象,支持多重属性的递归分析处理 nestedPa = getPropertyAccessorForPropertyPath(propertyName); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", ex); } // 经过上面的递归后,获取到最终需要操作的属性的对象,下面将根据该属性对象,获取最终要操作的内嵌对象的属性, // 生成PropertyTokenHolder,内省设置属性值 PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value)); }
getPropertyAccessorForPropertyPath根据属性名的表达式获取访问该属性的属性访问器AbstractNestablePropertyAccessor ,即BeanWrapperImpl 。下面的注释如果没有明白的话,请先往下看,然后再回头看这里,一切豁然开朗。
下面看一下 getPropertyAccessorForPropertyPath 是怎么处理内嵌属性的,即对象里面包含对象。
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) { // 获取第一个内嵌属性的位置,分隔符是"." int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath); // 递归获取内嵌属性,如果propertyPath不再存在分隔符“.”,返回递归结果 if (pos > -1) { // 假设propertyPath为 wheels[0].position String nestedProperty = propertyPath.substring(0, pos); // wheels[0].position -> wheels[0] String nestedPath = propertyPath.substring(pos + 1); // wheels[0].position -> position // 获取本轮递归中AbstractNestablePropertyAccessor的属性 AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty); return nestedPa.getPropertyAccessorForPropertyPath(nestedPath); } else { return this; } }
该方法递归获取需要访问属性的AbstractNestablePropertyAccessor。一起看一下getNestedPropertyAccessor是怎么根据属性名获取到属性访问器的。
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) { if (this.nestedPropertyAccessors == null) { this.nestedPropertyAccessors = new HashMap<String, AbstractNestablePropertyAccessor>(); } PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty); //根据属性名获取PropertyTokenHolder String canonicalName = tokens.canonicalName; Object value = getPropertyValue(tokens); // 根据PropertyTokenHolder获取该内嵌属性的实例化对象 // 该属性在bean中为null if (value == null || (value.getClass() == javaUtilOptionalClass && OptionalUnwrapper.isEmpty(value))) { // 如果允许自动创建属性,调用setDefaultValue创建默认的对象,否则抛异常 if (isAutoGrowNestedPaths()) { value = setDefaultValue(tokens); } else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName); } } // 把上面获取到的内嵌对象的实例,包裹为一个新的BeanWrapperImpl,然后把该BeanWrapperImpl缓存到本级的缓存对象nestedPropertyAccessors。 // 例如,上一节中的Car的driver属性,在car本级,维护了一个缓存对象nestedPropertyAccessors, // 第一次访问内嵌属性driver时,将会为driver创建PropertyAccessor,并缓存到nestedPropertyAccessors中。 AbstractNestablePropertyAccessor nestedPa = this.nestedPropertyAccessors.get(canonicalName); if (nestedPa == null || nestedPa.getWrappedInstance() != (value.getClass() == javaUtilOptionalClass ? OptionalUnwrapper.unwrap(value) : value)) { // 内嵌对象的实例,包裹为一个新的BeanWrapperImpl nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR); // 新的BeanWrapperImpl 继承本级的PropertyEditors. copyDefaultEditorsTo(nestedPa); copyCustomEditorsTo(nestedPa, canonicalName); // 缓存 this.nestedPropertyAccessors.put(canonicalName, nestedPa); } else { } return nestedPa; }
在上面代码中,getPropertyNameTokens将根据propertyName生成一个统一操作的结构PropertyTokenHolder,此类保存了属性名解析后的结构。该结构的成员如下:
属性类型及名称 | 说明 |
String canonicalName | propertyName为wheels[0]时,canonicalName为wheels[0]; propertyName为driver时,canonicalName为driver |
String actualName | propertyName为driver时,actualName为driver |
String[] keys | propertyName为driver时,keys为null |

上面的代码 Object value = getPropertyValue(tokens) 将根据 PropertyTokenHolder 获取指定property属性的实例,一起看下这段代码。
protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; // 获取PropertyHandler,内部实现是从cachedIntrospectionResults中取出该属性的PropertyDescriptor, // 然后取出属性的PropertyType, ReadMethod , WriteMethod PropertyHandler ph = getLocalPropertyHandler(actualName); // 属性不可读,抛异常 if (ph == null || !ph.isReadable()) { throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName); } try { // 内省方式获取属性的实例 Object value = ph.getValue(); // 如果该属性是数组、list、set,tokens的keys是不为空的,keys将会保存需要访问的索引号, // 在map中,keys是一个字符串 // 下面就是通过该索引号获取特定下标的属性值。 if (tokens.keys != null) { if (value == null) { if (isAutoGrowNestedPaths()) { value = setDefaultValue(tokens.actualName); } else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value of property referenced in indexed " + "property path '" + propertyName + "': returned null"); } } String indexedPropertyName = tokens.actualName; // apply indexes and map keys for (int i = 0; i < tokens.keys.length; i++) { String key = tokens.keys[i]; if (value == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value of property referenced in indexed " + "property path '" + propertyName + "': returned null"); } else if (value.getClass().isArray()) { //处理数组 int index = Integer.parseInt(key); value = growArrayIfNecessary(value, index, indexedPropertyName); value = Array.get(value, index); } else if (value instanceof List) { //处理list int index = Integer.parseInt(key); List<Object> list = (List<Object>) value; growCollectionIfNecessary(list, index, indexedPropertyName, ph, i + 1); value = list.get(index); } else if (value instanceof Set) { //处理set // Apply index to Iterator in case of a Set. Set<Object> set = (Set<Object>) value; int index = Integer.parseInt(key); Iterator<Object> it = set.iterator(); for (int j = 0; it.hasNext(); j++) { Object elem = it.next(); if (j == index) { value = elem; break; } } } else if (value instanceof Map) { //处理map Map<Object, Object> map = (Map<Object, Object>) value; Class<?> mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(0); // IMPORTANT: Do not pass full property name in here - property editors // must not kick in for map keys but rather only for map values. TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType); Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); value = map.get(convertedMapKey); } else { } indexedPropertyName += PROPERTY_KEY_PREFIX + key + PROPERTY_KEY_SUFFIX; } } return value; } //异常,忽略 }
总结:在通过setPropertyValue设置bean的属性时,首先将会根据propertyName的字符串,递归获取该属性所在的内嵌属性(假如属性不在内嵌属性,获取的就是它自己),然后通过内省的方式设置该属性的值。
3.3 获取属性
BeanWrapperImpl有两个获取属性的方法
public Object getPropertyValue(String propertyName) throws BeansException protected Object getPropertyValue(PropertyTokenHolder tokens)
其中 getPropertyValue(PropertyTokenHolder tokens) 在上面设置属性那一节已经出现过了,它是在内部使用的,不对外公开。
既然如此,看一下Object getPropertyValue(String propertyName)这个方法吧。该方法在AbstractNestablePropertyAccessor中实现。
public Object getPropertyValue(String propertyName) throws BeansException { AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName); PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); return nestedPa.getPropertyValue(tokens); }
其中,getPropertyAccessorForPropertyPath方法在上一节中已经有详细的解析,它将递归获取可以访问该属性的AbstractNestablePropertyAccessor,这里的实现类是BeanWrapperImpl。例如propertyName是driver.age,那么BeanWrapperImpl所包裹的就是Driver的实例。然后,根据PropertyTokenHolder获取属性值。这个流程与设置属性的相似,请参考前面。