spring 4.x源码分析-BeanWrapper


声明:本文转载自https://my.oschina.net/thinwonton/blog/1492224,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

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获取属性值。这个流程与设置属性的相似,请参考前面。

 

本文发表于2017年07月31日 10:36
(c)注:本文转载自https://my.oschina.net/thinwonton/blog/1492224,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2065 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1