Spring读源码系列番外篇08---BeanWrapper没有那么简单--上
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring读源码系列番外篇08---BeanWrapper没有那么简单--上相关的知识,希望对你有一定的参考价值。
Spring读源码系列番外篇08---BeanWrapper没有那么简单--上
- 引子
- PropertyAccessor
- TypeConverter
- PropertyAccessor使用Demo
- PropertyValue的作用什么?
- PropertyTokenHolder的作用是什么?
- 总结
引子
大部分人对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的结构体系剖析---上