Spring读源码系列番外篇08---BeanWrapper没有那么简单--上

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring读源码系列番外篇08---BeanWrapper没有那么简单--上相关的知识,希望对你有一定的参考价值。

Spring读源码系列番外篇08---BeanWrapper没有那么简单--上


引子

大部分人对BeanWrapper的认知都停留在其包装了创建后的bean实例这一条上,但是事实真的如你想象的那么简单吗?


显然从继承图都可以看出没那么简单,那么下面就让我们去剖析一下吧:

PropertyEditorRegistry和PropertyEditorRegistrySupport两个类的用法,看该篇文章
Spring读源码系列番外篇—06----类型转换—下—ConversionService相关家族


PropertyAccessor

since 1.1

属性访问器PropertyAccessor接口的作用是存/取Bean对象的属性。为了体现这个接口它的重要性,据我目前了解我此处贴出这么一句话:

所有Spring创建的Bean对象都使用该接口存取Bean属性值

它是可以访问命名属性named properties(例如对象的bean属性或对象中的字段)的类的公共接口。大名鼎鼎的BeanWrapper接口也继承自它,它所在包是org.springframework.beans(BeanWrapper也在此包)

//可以访问命名属性(例如对象的 bean 属性或对象中的字段)的类的通用接口用作 BeanWrapper 的基本接口。
public interface PropertyAccessor 

	/**
嵌套属性的路径分隔符。遵循正常的 Java 约定:getFoo().getBar() 将是“foo.bar”。
	 */
	String NESTED_PROPERTY_SEPARATOR = ".";

	char NESTED_PROPERTY_SEPARATOR_CHAR = '.';

	/**
指示索引或映射属性(如“person.addresses[0]”)的属性键开始的标记。
	 */
	String PROPERTY_KEY_PREFIX = "[";

	char PROPERTY_KEY_PREFIX_CHAR = '[';

	String PROPERTY_KEY_SUFFIX = "]";

	char PROPERTY_KEY_SUFFIX_CHAR = ']';


	/**
确定指定的属性是否可读。如果属性不存在,则返回 false。
	 */
	boolean isReadableProperty(String propertyName);

	/**
确定指定的属性是否可写。如果属性不存在,则返回 false
	 */
	boolean isWritableProperty(String propertyName);

	/**
获取属性名对应的属性类型
	 */
	@Nullable
	Class<?> getPropertyType(String propertyName) throws BeansException;

	/**
返回的是属性的描述符
	 */
	@Nullable
	TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;

	/**
返回属性值
	 */
	@Nullable
	Object getPropertyValue(String propertyName) throws BeansException;

	/**
设置属性值
	 */
	void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;

	/**
同上
	 */
	void setPropertyValue(PropertyValue pv) throws BeansException;

	/**
批量更新
	 */
	void setPropertyValues(Map<?, ?> map) throws BeansException;

	/**
执行批量更新的首选方式。
请注意,执行批量更新与执行单个更新不同,如果遇到可恢复的错误(例如类型不匹配,但不是无效的字段名称等),
此类的实现将继续更新属性,抛出包含所有单个错误的 PropertyBatchUpdateException。
稍后可以检查此异常以查看所有绑定错误。
成功更新的属性保持更改。不允许未知字段或无效字段。
	 */
	void setPropertyValues(PropertyValues pvs) throws BeansException;

	/**
新增一个是否忽略不认识的字段
	 */
	void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
			throws BeansException;

	/**
又新增一个是否忽略不合法的属性(found but not accessible)
	 */
	void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException;



该类主要规定了有关属性可读可写判断,属性获取,属性更新的一些接口

最终的实现类主要有DirectFieldAccessor和BeanWrapperImpl

说明一下:DirectFieldAccessFallbackBeanWrapper它在spring-data-commons这个jar里面,所以若你没有使用spring-data-xxx是木有此实现类的~~~


TypeConverter

Since: 2.0 :该类是2.0出来的,当时spring全新一代类型转换器接口还没出来,因此该类一开始主要和PropertyEditor打配合

/**
定义类型转换方法的接口。通常(但不一定)与 PropertyEditorRegistry 接口一起实现。
注意:由于 TypeConverter 实现通常基于非线程安全的 PropertyEditor,因此 TypeConverter 本身也不被视为线程安全的
 */
public interface TypeConverter 

	/**
将值转换为所需的类型(如果需要,从字符串)。
从 String 到任何类型的转换通常会使用 PropertyEditor 类的 setAsText 方法,
或 ConversionService 中的 Spring Converter。
	 */
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;

	/**
和上面相比,就多了一个MethodParameter: 作为转换目标的方法参数(用于分析泛型类型;可能为 null)
	 */
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable MethodParameter methodParam) throws TypeMismatchException;

	/**
Field : 作为转换目标的反射字段(用于分析泛型类型;可能为 null)
	 */
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
			throws TypeMismatchException;

	/**
	 TypeDescriptor :要使用的类型描述符(可能为 null))
	 */
	@Nullable
	default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException 

		throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
	


TypeConverter 由该类负责提供一个统一的类型转换接口,底层会调用PropertyEditor或者ConvertService来找到具体某个转换器,来执行真正的转化操作


ConfigurablePropertyAccessor

可配置的PropertyAccessor。它是一个子接口,提供了可配置的能力,并且它还继承了PropertyEditorRegistry、TypeConverter等接口~~~

/**
封装 PropertyAccessor 的配置方法的接口。
还扩展了 PropertyEditorRegistry 接口,该接口定义了 PropertyEditor 管理的方法。
作为 BeanWrapper 的基本接口。
 */
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter 

	// 设置一个ConversionService ,用于对value值进行转换
	// 它是Spring3.0后推出来替代属性编辑器PropertyEditors的方案~
	void setConversionService(@Nullable ConversionService conversionService);
	@Nullable
	ConversionService getConversionService();

	/**
设置在将属性编辑器应用于属性的新值时是否提取旧属性值。
	 */
	void setExtractOldValueForEditor(boolean extractOldValueForEditor);

	// 设置在将属性编辑器应用于属性的新值时是**否提取旧属性值**。
	boolean isExtractOldValueForEditor();

	// 设置此实例是否应尝试“自动增长”包含null的嵌套路径。
	// true:为null的值会自动被填充为一个默认的value值,而不是抛出异常NullValueInNestedPathException
	void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
	boolean isAutoGrowNestedPaths();


ConfigurablePropertyAccessor具有属性获取相关功能,还有PropertyEditor注册中心相关功能,还有类型转换统一接口的相关功能,并且还适配了新一代spring类型转换服务管理器


TypeConverterSupport

/**
TypeConverter 接口的基本实现,使用包私有委托。主要作为 BeanWrapperImpl 的基类。
TypeConverterSupport有PropertyEditor管理的功能和统一使用类型转换接口的功能
 * @since 3.2
 */
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter 
    //类型转换的委托类,说明它才是干活的家伙
	@Nullable
	TypeConverterDelegate typeConverterDelegate;


	@Override
	@Nullable
	public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException 
		return convertIfNecessary(value, requiredType, TypeDescriptor.valueOf(requiredType));
	

	@Override
	@Nullable
	public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable MethodParameter methodParam) throws TypeMismatchException 

		return convertIfNecessary(value, requiredType,
				(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
	

	@Override
	@Nullable
	public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
			throws TypeMismatchException 

		return convertIfNecessary(value, requiredType,
				(field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType)));
	


//最终调用的方法
	@Nullable
	@Override
	public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException 

		Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
		try 
		//最终是由内部这个代理类型转换器对象进行类型转换的相关处理
			return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
		
		catch (ConverterNotFoundException | IllegalStateException ex) 
			throw new ConversionNotSupportedException(value, requiredType, ex);
		
		catch (ConversionException | IllegalArgumentException ex) 
			throw new TypeMismatchException(value, requiredType, ex);
		
	


PropertyEditorRegistrySupport已经对PropertyEditorRegistry中规定的PropertyEditor管理相关接口进行了实现,因此TypeConverterSupport继承了他,也就有了PropertyEditor管理相关的功能,因此TypeConverterSupport仅需要实现TypeConverter接口即可


TypeConverterDelegate

/**
Internal helper class for converting property values to target types.

Works on a given PropertyEditorRegistrySupport instance. 

Used as a delegate by BeanWrapperImpl and SimpleTypeConverter.

since 2.0
 */
class TypeConverterDelegate 

	private static final Log logger = LogFactory.getLog(TypeConverterDelegate.class);
    //通过PropertyEditorRegistrySupport来查找propertyEditor类型的转换器
	private final PropertyEditorRegistrySupport propertyEditorRegistry;
    //目标对象
	@Nullable
	private final Object targetObject;

	public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry) 
		this(propertyEditorRegistry, null);
	
	
	public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry, @Nullable Object targetObject) 
		this.propertyEditorRegistry = propertyEditorRegistry;
		this.targetObject = targetObject;
	


	@Nullable
	public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue,
			Object newValue, @Nullable Class<T> requiredType) throws IllegalArgumentException 

		return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
	

	/**
	 * Convert the value to the required type (if necessary from a String),
	 * for the specified property.
	 */
	@SuppressWarnings("unchecked")
	@Nullable
	public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
			@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException 
        
		// Custom editor for this type?
		//先去注册中心propertyEditorRegistry寻找合适的PropertyEditor
		//先去自定义集合中寻找
		//这里指定了propertyPath
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// No custom editor but custom ConversionService specified?
		//为了整合spring3.0的全新转换器ConversionService
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
		//如果PropertyEditor没找到,但是ConversionService存在
		//newValue和typeDescriptor必须头存在才行,因此下面才会有第二次尝试调用conversionService ,但是条件宽松了很多
		//相当于做个兜底
		if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) 
			TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			//判断当前conversionService能否对这个类型对进行类型转换
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) 
				try 
				//如果可以的话,会尝试进行类型转换
					return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
				
				catch (ConversionFailedException ex) 
					// fallback to default conversion logic below
					conversionAttemptEx = ex;
				
			
		

        //newValue是需要进行转换的值
		Object convertedValue = newValue;
        
		// Value not of required type?
		//因为大部分情况可能不需要进行类型转换,这里就是进行判断,看是否需要的类型就是拿到的字符串类型
		if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) 
			if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
					convertedValue instanceof String) 
					//requiredType是个集合---即当前需要将String--->Collection
					//拿到集合里面元素的类型
				TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
				if (elementTypeDesc != null) 
					Class<?> elementType = elementTypeDesc.getType();
					if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) 
					//按照分割符进行分割---返回一个String[],分隔符为","
						convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
					
				
			
			//propertyEditor找不到
			//上面一开始是去自定义集合里面寻找,没找到再去conversionService里面找
			//最后才是默认的PropertyEditor集合
			if (editor == null) 
			//寻找一个默认的PropertyEditor---有兜底转换器存在
				editor = findDefaultEditor(requiredType);
			
			//进行转换,用的是找到的PropertyEditor将string--->某个对象类型
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		

		boolean standardConversion = false;
         //上面如果没有合适的转换器,并且需要的类型不为空,那么下面会进行一些标准的转化流程 
		if (requiredType != null) 
			// Try to apply some standard type conversion rules if appropriate.
			if (convertedValue != null) 
			//需要的是一个Object类型的对象,直接返回即可
				if (Object.class == requiredType) 
					return (T) convertedValue;
				
				//期望得到数组类型---进行相关处理
				else if (requiredType.isArray()) 
					// Array required -> apply appropriate conversion of elements.
					if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) 
						convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
					
					return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
				
				//期望得到集合类型
				else if (convertedValue instanceof Collection) 
					// Convert elements to target type, if determined.
					convertedValue = convertToTypedCollection(
							(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				
				//期望得到map类型
				else if (convertedValue instanceof Map) 
					// Convert keys and values to respective target type, if determined.
					convertedValue = convertToTypedMap(
							(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				
				if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) 
					convertedValue = Array.get(convertedValue, 0);
					standardConversion = true;
				
				//期望的竟然是string类型,只要原对象不是原生类型,那么调用toString直接返回即可
				if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) 
					// We can stringify any primitive value...
					return (T) convertedValue.toString();
				
				//转化后得到的是String,但是期望的类型并不是string,并且期望的类型不是接口也不是枚举
				else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) 
					iconversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) 
						try 
						//尝试去实例化目标对象然后返回,convertedValue作为构造器的参数传入
							Constructor<T> strCtor = requiredType.getConstructor(String.class);
							//这里是把convertedValue当做构造参数传入
							return BeanUtils.instantiateClass(strCtor, convertedValue);
						
						cat

以上是关于Spring读源码系列番外篇08---BeanWrapper没有那么简单--上的主要内容,如果未能解决你的问题,请参考以下文章

Spring读源码系列番外篇08---BeanWrapper没有那么简单--中

Spring读源码系列番外篇---06----类型转换---下---ConversionService相关家族

Spring读源码系列番外篇---03---PropertyResolver的结构体系剖析---下

Spring读源码系列番外篇---02---PropertyResolver的结构体系剖析---上

Spring读源码系列番外篇---05----类型转换---中---三种全新的类型转换器

重学springboot系列番外篇之RestTemplate