Spring读源码系列番外篇08---BeanWrapper没有那么简单--上
- 引子
- PropertyAccessor
- TypeConverter
- ConfigurablePropertyAccessor
- TypeConverterSupport
- TypeConverterDelegate
- AbstractPropertyAccessor
- AbstractNestablePropertyAccessor
- DirectFieldAccessor
- AbstractNestablePropertyAccessor
- 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也在此包)
代码语言:javascript复制//可以访问命名属性(例如对象的 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打配合
代码语言:javascript复制/**
定义类型转换方法的接口。通常(但不一定)与 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等接口~~~
代码语言:javascript复制/**
封装 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
代码语言:javascript复制/**
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
代码语言:javascript复制/**
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);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" requiredType.getName() "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" requiredType.getName() "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
//需要的是枚举类型
if (requiredType.isEnum() && trimmedValue.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
//convertedValue 是数字类型并且期望的类型也是数字类型
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null
if (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
}
//走到这里,按理来说,此时convertedValue应该已经被转换为了requiredType类型了
//即如果转换成功了,这个分支就进不去
//如果convertedValue不是requiredType类型,说明还是没转换成功
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
//是否发生了异常
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
//最后再尝试用conversionService做一下兜底转换,前提是conversionService被设置了
else if (conversionService != null && typeDescriptor != null) {
// ConversionService not tried before, probably custom editor found
// but editor couldn't produce the required type...
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
//走到这里,很遗憾,转换失败,准备抛出转换异常
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append(''');
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append(''');
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type '").append(
ClassUtils.getDescriptiveType(convertedValue)).append(''');
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since "
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
//返回转换后的值,也可能不需要进行类型转换,原样返回
return (T) convertedValue;
}
/**
* Find a default editor for the given type.
* @param requiredType the type to find an editor for
* @return the corresponding editor, or {@code null} if none
*/
@Nullable
private PropertyEditor findDefaultEditor(@Nullable Class<?> requiredType) {
PropertyEditor editor = null;
if (requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors.
//先去overriddenDefaultEditors集合找,然后再去defaultEditors集合找
editor = this.propertyEditorRegistry.getDefaultEditor(requiredType);
if (editor == null && String.class != requiredType) {
// No BeanWrapper default editor -> check standard JavaBean editor.
editor = BeanUtils.findEditorByConvention(requiredType);
}
}
return editor;
}
/**
* Convert the value to the required type (if necessary from a String),
* using the given property editor.
* @param oldValue the previous value, if available (may be {@code null})
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or {@code null} if not known, for example in case of a collection element)
* @param editor the PropertyEditor to use
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@Nullable
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
Object convertedValue = newValue;
if (editor != null && !(convertedValue instanceof String)) {
// Not a String -> use PropertyEditor's setValue.
// With standard PropertyEditors, this will return the very same object;
// we just want to allow special PropertyEditors to override setValue
// for type conversion from non-String values to the required type.
try {
editor.setValue(convertedValue);
Object newConvertedValue = editor.getValue();
if (newConvertedValue != convertedValue) {
convertedValue = newConvertedValue;
// Reset PropertyEditor: It already did a proper conversion.
// Don't use it again for a setAsText call.
editor = null;
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" editor.getClass().getName() "] does not support setValue call", ex);
}
// Swallow and proceed.
}
}
Object returnValue = convertedValue;
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
if (logger.isTraceEnabled()) {
logger.trace("Converting String array to comma-delimited String [" convertedValue "]");
}
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
}
if (convertedValue instanceof String) {
if (editor != null) {
// Use PropertyEditor's setAsText in case of a String value.
if (logger.isTraceEnabled()) {
logger.trace("Converting String to [" requiredType "] using property editor [" editor "]");
}
String newTextValue = (String) convertedValue;
return doConvertTextValue(oldValue, newTextValue, editor);
}
else if (String.class == requiredType) {
returnValue = convertedValue;
}
}
return returnValue;
}
/**
* Convert the given text value using the given property editor.
* @param oldValue the previous value, if available (may be {@code null})
* @param newTextValue the proposed text value
* @param editor the PropertyEditor to use
* @return the converted value
*/
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" editor.getClass().getName() "] does not support setValue call", ex);
}
// Swallow and proceed.
}
editor.setAsText(newTextValue);
return editor.getValue();
}
//剩下的一些源码都不是特别重要了,感兴趣的自己去翻翻吧
//。。。。
该委托类的主要职责就是利用PropertyEditorRegistrySupport来完成类型转换工作,然后返回最终转换成功得到的结果
这样一来,TypeConverterSupport类中代码就少了很多,保证核心代码的整洁与解耦,个人觉得设计的挺不错的,我们可以借鉴这种思想
AbstractPropertyAccessor
对于AbstractPropertyAccessor来说,因为他继承了TypeConverterSupport类,因此关于类型转换功能,和转换器注册中心的功能就不需要他实现了,他只需要实现PropertyAccessor规定的相关属性获取接口和ConfigurablePropertyAccessor新增的一些接口即可
代码语言:javascript复制/**
* Abstract implementation of the PropertyAccessor interface.
* Provides base implementations of all convenience methods, with the
* implementation of actual property access left to subclasses.
*/
public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
private boolean extractOldValueForEditor = false;
private boolean autoGrowNestedPaths = false;
boolean suppressNotWritablePropertyException = false;
//ConfigurablePropertyAccessor接口中新增的方法
@Override
public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
this.extractOldValueForEditor = extractOldValueForEditor;
}
@Override
public boolean isExtractOldValueForEditor() {
return this.extractOldValueForEditor;
}
@Override
public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
this.autoGrowNestedPaths = autoGrowNestedPaths;
}
@Override
public boolean isAutoGrowNestedPaths() {
return this.autoGrowNestedPaths;
}
//PropertyAccessor中规定的属性获取接口实现
//单个属性进行更新
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
setPropertyValue(pv.getName(), pv.getValue());
}
@Override
public void setPropertyValues(Map<?, ?> map) throws BeansException {
setPropertyValues(new MutablePropertyValues(map));
}
@Override
public void setPropertyValues(PropertyValues pvs) throws BeansException {
setPropertyValues(pvs, false, false);
}
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException {
setPropertyValues(pvs, ignoreUnknown, false);
}
//批量更新
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
if (ignoreUnknown) {
this.suppressNotWritablePropertyException = true;
}
try {
//遍历属性集合
for (PropertyValue pv : propertyValues) {
// setPropertyValue may throw any BeansException, which won't be caught
// here, if there is a critical failure such as no matching field.
// We can attempt to deal only with less serious exceptions.
try {
//挨个更新
setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
if (!ignoreUnknown) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (NullValueInNestedPathException ex) {
if (!ignoreInvalid) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (PropertyAccessException ex) {
if (propertyAccessExceptions == null) {
propertyAccessExceptions = new ArrayList<>();
}
propertyAccessExceptions.add(ex);
}
}
}
finally {
if (ignoreUnknown) {
this.suppressNotWritablePropertyException = false;
}
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);
throw new PropertyBatchUpdateException(paeArray);
}
}
// Redefined with public visibility.
@Override
@Nullable
public Class<?> getPropertyType(String propertyPath) {
return null;
}
// 抽象方法: 具体的get/set方法由子类去实现的~~
@Override
@Nullable
public abstract Object getPropertyValue(String propertyName) throws BeansException;
@Override
public abstract void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
}
getPropertyType,getPropertyValue和setPropertyValue这三个关系具体属性操作的方法留给了子类去实现,而该抽象类主要覆写了同时对多个属性进行更新的方法
ConversionService的获取与设置方法也没有进行实现
AbstractNestablePropertyAccessor
一个典型的实现,为其它所有使用案例提供必要的基础设施。nestable:可嵌套的,支持嵌套的
当BeanWrapperImpi继承了AbstractNestablePropertyAccessor后,就具有了对属性操作,属性转换的基础功能,还需要实现的就剩下了BeanWrapper接口了
代码语言:javascript复制//一个基本的 ConfigurablePropertyAccessor,为所有典型用例提供必要的基础设施
//如有必要,此访问器会将集合和数组值转换为相应的目标集合或数组。
//处理集合或数组的自定义属性编辑器可以通过 PropertyEditor 的 setValue 编写,
//也可以通过 setAsText 针对逗号分隔的字符串编写,因为如果数组本身不可分配,则字符串数组将以这种格式转换。
public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor {
private static final Log logger = LogFactory.getLog(AbstractNestablePropertyAccessor.class);
private int autoGrowCollectionLimit = Integer.MAX_VALUE;
@Nullable
Object wrappedObject;
private String nestedPath = "";
@Nullable
Object rootObject;
/** Map with cached nested Accessors: nested path -> Accessor instance. */
@Nullable
private Map<String, AbstractNestablePropertyAccessor> nestedPropertyAccessors;
//默认是注册默认的属性编辑器的:defaultEditors 它几乎处理了所有的Java内置类型 包括基本类型、包装类型以及对应数组类型
protected AbstractNestablePropertyAccessor() {
this(true);
}
protected AbstractNestablePropertyAccessor(boolean registerDefaultEditors) {
if (registerDefaultEditors) {
registerDefaultEditors();
}
//继承至父类TypeConverterSupport中的转换器代理镀锡,由它负责从注册中心寻找转换器
//然后执行转换工作,因为在父类中没有进行初始化工作,而是延迟到子类中进行初始化
this.typeConverterDelegate = new TypeConverterDelegate(this);
}
protected AbstractNestablePropertyAccessor(Object object) {
registerDefaultEditors();
//对外部传入需要包裹的对象进行包裹处理
setWrappedInstance(object);
}
protected AbstractNestablePropertyAccessor(Class<?> clazz) {
registerDefaultEditors();
setWrappedInstance(BeanUtils.instantiateClass(clazz));
}
/**
* Create a new accessor for the given object,
* registering a nested path that the object is in.
*/
protected AbstractNestablePropertyAccessor(Object object, String nestedPath, Object rootObject) {
registerDefaultEditors();
setWrappedInstance(object, nestedPath, rootObject);
}
protected AbstractNestablePropertyAccessor(Object object, String nestedPath, AbstractNestablePropertyAccessor parent) {
setWrappedInstance(object, nestedPath, parent.getWrappedInstance());
setExtractOldValueForEditor(parent.isExtractOldValueForEditor());
setAutoGrowNestedPaths(parent.isAutoGrowNestedPaths());
setAutoGrowCollectionLimit(parent.getAutoGrowCollectionLimit());
setConversionService(parent.getConversionService());
}
/**
* Specify a limit for array and collection auto-growing.
* <p>Default is unlimited on a plain accessor.
*/
public void setAutoGrowCollectionLimit(int autoGrowCollectionLimit) {
this.autoGrowCollectionLimit = autoGrowCollectionLimit;
}
/**
* Return the limit for array and collection auto-growing.
*/
public int getAutoGrowCollectionLimit() {
return this.autoGrowCollectionLimit;
}
/**
* Switch the target object, replacing the cached introspection results only
* if the class of the new object is different to that of the replaced object.
* @param object the new target object
*/
public void setWrappedInstance(Object object) {
setWrappedInstance(object, "", null);
}
// wrappedObject:目标对象
public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) {
this.wrappedObject = ObjectUtils.unwrapOptional(object);
Assert.notNull(this.wrappedObject, "Target object must not be null");
this.nestedPath = (nestedPath != null ? nestedPath : "");
// 此处根对象,若nestedPath存在的话,是可以自定义一个rootObject的~~~
this.rootObject = (!this.nestedPath.isEmpty() ? rootObject : this.wrappedObject);
this.nestedPropertyAccessors = null;
this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
}
public final Object getWrappedInstance() {
Assert.state(this.wrappedObject != null, "No wrapped object");
return this.wrappedObject;
}
public final Class<?> getWrappedClass() {
return getWrappedInstance().getClass();
}
/**
* Return the nested path of the object wrapped by this accessor.
*/
public final String getNestedPath() {
return this.nestedPath;
}
/**
* Return the root object at the top of the path of this accessor.
* @see #getNestedPath
*/
public final Object getRootInstance() {
Assert.state(this.rootObject != null, "No root object");
return this.rootObject;
}
/**
* Return the class of the root object at the top of the path of this accessor.
* @see #getNestedPath
*/
public final Class<?> getRootClass() {
return getRootInstance().getClass();
}
@Override
public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
AbstractNestablePropertyAccessor nestedPa;
try {
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath propertyName,
"Nested property in path '" propertyName "' does not exist", ex);
}
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
AbstractNestablePropertyAccessor nestedPa;
try {
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath propertyName,
"Nested property in path '" propertyName "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
if (nestedPa == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
nestedPa.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
if (tokens.keys != null) {
processKeyedProperty(tokens, pv);
}
else {
processLocalProperty(tokens, pv);
}
}
@Override
@Nullable
public Class<?> getPropertyType(String propertyName) throws BeansException {
try {
PropertyHandler ph = getPropertyHandler(propertyName);
if (ph != null) {
return ph.getPropertyType();
}
else {
// Maybe an indexed/mapped property...
Object value = getPropertyValue(propertyName);
if (value != null) {
return value.getClass();
}
// Check to see if there is a custom editor,
// which might give an indication on the desired target type.
Class<?> editorType = guessPropertyTypeFromEditors(propertyName);
if (editorType != null) {
return editorType;
}
}
}
catch (InvalidPropertyException ex) {
// Consider as not determinable.
}
return null;
}
@Override
@Nullable
public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
try {
AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName);
String finalPath = getFinalPath(nestedPa, propertyName);
PropertyTokenHolder tokens = getPropertyNameTokens(finalPath);
PropertyHandler ph = nestedPa.getLocalPropertyHandler(tokens.actualName);
if (ph != null) {
if (tokens.keys != null) {
if (ph.isReadable() || ph.isWritable()) {
return ph.nested(tokens.keys.length);
}
}
else {
if (ph.isReadable() || ph.isWritable()) {
return ph.toTypeDescriptor();
}
}
}
}
catch (InvalidPropertyException ex) {
// Consider as not determinable.
}
return null;
}
@Override
public boolean isReadableProperty(String propertyName) {
try {
PropertyHandler ph = getPropertyHandler(propertyName);
if (ph != null) {
return ph.isReadable();
}
else {
// Maybe an indexed/mapped property...
getPropertyValue(propertyName);
return true;
}
}
catch (InvalidPropertyException ex) {
// Cannot be evaluated, so can't be readable.
}
return false;
}
@Override
public boolean isWritableProperty(String propertyName) {
try {
PropertyHandler ph = getPropertyHandler(propertyName);
if (ph != null) {
return ph.isWritable();
}
else {
// Maybe an indexed/mapped property...
getPropertyValue(propertyName);
return true;
}
}
catch (InvalidPropertyException ex) {
// Cannot be evaluated, so can't be writable.
}
return false;
}
@Nullable
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue,
@Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td)
throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
//真正进行类型转换工作的依旧是代理类
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
//异常逻辑处理...
}
//为属性做类型转换
@Nullable
protected Object convertForProperty(
String propertyName, @Nullable Object oldValue, @Nullable Object newValue, TypeDescriptor td)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
//获取某个属性的值
@Override
@Nullable
public Object getPropertyValue(String propertyName) throws BeansException {
AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName);
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
return nestedPa.getPropertyValue(tokens);
}
@SuppressWarnings("unchecked")
@Nullable
protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
PropertyHandler ph = getLocalPropertyHandler(actualName);
if (ph == null || !ph.isReadable()) {
throw new NotReadablePropertyException(getRootClass(), this.nestedPath propertyName);
}
try {
Object value = ph.getValue();
if (tokens.keys != null) {
if (value == null) {
if (isAutoGrowNestedPaths()) {
value = setDefaultValue(new PropertyTokenHolder(tokens.actualName));
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath propertyName,
"Cannot access indexed value of property referenced in indexed "
"property path '" propertyName "': returned null");
}
}
StringBuilder indexedPropertyName = new StringBuilder(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.toString());
value = Array.get(value, index);
}
else if (value instanceof List) {
int index = Integer.parseInt(key);
List<Object> list = (List<Object>) value;
growCollectionIfNecessary(list, index, indexedPropertyName.toString(), ph, i 1);
value = list.get(index);
}
else if (value instanceof Set) {
// Apply index to Iterator in case of a Set.
Set<Object> set = (Set<Object>) value;
int index = Integer.parseInt(key);
if (index < 0 || index >= set.size()) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath propertyName,
"Cannot get element with index " index " from Set of size "
set.size() ", accessed using property path '" propertyName "'");
}
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<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 {
throw new InvalidPropertyException(getRootClass(), this.nestedPath propertyName,
"Property referenced in indexed property path '" propertyName
"' is neither an array nor a List nor a Set nor a Map; returned value was [" value "]");
}
indexedPropertyName.append(PROPERTY_KEY_PREFIX).append(key).append(PROPERTY_KEY_SUFFIX);
}
}
return value;
}
//...处理异常逻辑
}
...// 简单的说,它会处理.逻辑以及[0]等逻辑 [0]对应着集合和数组都可
/**
* A handler for a specific property.
*/
protected abstract static class PropertyHandler {
private final Class<?> propertyType;
private final boolean readable;
private final boolean writable;
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
this.propertyType = propertyType;
this.readable = readable;
this.writable = writable;
}
public Class<?> getPropertyType() {
return this.propertyType;
}
public boolean isReadable() {
return this.readable;
}
public boolean isWritable() {
return this.writable;
}
public abstract TypeDescriptor toTypeDescriptor();
public abstract ResolvableType getResolvableType();
@Nullable
public Class<?> getMapKeyType(int nestingLevel) {
return getResolvableType().getNested(nestingLevel).asMap().resolveGeneric(0);
}
@Nullable
public Class<?> getMapValueType(int nestingLevel) {
return getResolvableType().getNested(nestingLevel).asMap().resolveGeneric(1);
}
@Nullable
public Class<?> getCollectionType(int nestingLevel) {
return getResolvableType().getNested(nestingLevel).asCollection().resolveGeneric();
}
@Nullable
public abstract TypeDescriptor nested(int level);
@Nullable
public abstract Object getValue() throws Exception;
public abstract void setValue(@Nullable Object value) throws Exception;
}
/**
* Holder class used to store property tokens.
*/
protected static class PropertyTokenHolder {
public PropertyTokenHolder(String name) {
this.actualName = name;
this.canonicalName = name;
}
public String actualName;
public String canonicalName;
@Nullable
public String[] keys;
}
}
此访问器将集合和数组值转换为相应的目标集合或数组,当然还解决了级联属性(嵌套属性)的问题~
需要特别注意的是:AbstractNestablePropertyAccessor这个抽象类在Spring4.2后才提供~~~
该类源码1000多行,特别长,所以这里对源码删减了一大半,方便可以抓住重点实现
DirectFieldAccessor
它继承自AbstractNestablePropertyAccessor,所以它肯定也就可以处理级联属性和集合数组值了。(请注意,在Spring4.2之后支持,之前是不支持的~)
直接访问实例字段的 ConfigurablePropertyAccessor 实现。允许直接绑定到字段而不是通过 JavaBean 的setter方法。
从 Spring 4.2 开始,绝大多数 BeanWrapper 功能都已合并到 AbstractPropertyAccessor,这意味着这里现在也支持属性遍历以及集合和映射访问。
DirectFieldAccessor 的“extractOldValueForEditor”设置的默认值为“true”,因为始终可以读取字段而没有副作用。
代码语言:javascript复制public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
//缓存字段对应的字段属性管理器
private final Map<String, FieldPropertyHandler> fieldMap = new HashMap<>();
public DirectFieldAccessor(Object object) {
super(object);
}
// 这个构造器也是protected 的 所以若你想自己指定nestedPath和parent,你可以继承此类~~~
protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor parent) {
super(object, nestedPath, parent);
}
@Override
@Nullable
protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) {
//先从缓存中拿取
FieldPropertyHandler propertyHandler = this.fieldMap.get(propertyName);
if (propertyHandler == null) {
//缓存没有,先反射获取对应字段,然后再构造该属性对应的属性管理器
//然后加入缓存,并返回这个属性管理器
Field field = ReflectionUtils.findField(getWrappedClass(), propertyName);
if (field != null) {
propertyHandler = new FieldPropertyHandler(field);
this.fieldMap.put(propertyName, propertyHandler);
}
}
return propertyHandler;
}
// 实现父类的抽象方法,依旧使用DirectFieldAccessor去处理~~~
@Override
protected DirectFieldAccessor newNestedPropertyAccessor(Object object, String nestedPath) {
return new DirectFieldAccessor(object, nestedPath, this);
}
@Override
protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) {
PropertyMatches matches = PropertyMatches.forField(propertyName, getRootClass());
throw new NotWritablePropertyException(getRootClass(), getNestedPath() propertyName,
matches.buildErrorMessage(), matches.getPossibleMatches());
}
// 字段field属性处理器,使用内部类实现PropertyHandler ~~~
private class FieldPropertyHandler extends PropertyHandler {
//管理的属性
private final Field field;
public FieldPropertyHandler(Field field) {
super(field.getType(), true, true);
this.field = field;
}
//获取当前属性的描述符号
@Override
public TypeDescriptor toTypeDescriptor() {
return new TypeDescriptor(this.field);
}
//获取当前属性类型相关信息
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forField(this.field);
}
@Override
@Nullable
public TypeDescriptor nested(int level) {
return TypeDescriptor.nested(this.field, level);
}
//获取指定对象实例某个字段的值---该方法直接反射获取,不需要通过get方法
@Override
@Nullable
public Object getValue() throws Exception {
try {
ReflectionUtils.makeAccessible(this.field);
return this.field.get(getWrappedInstance());
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getWrappedClass(),
this.field.getName(), "Field is not accessible", ex);
}
}
//直接反射设置,不需要通过set方法
@Override
public void setValue(@Nullable Object value) throws Exception {
try {
ReflectionUtils.makeAccessible(this.field);
this.field.set(getWrappedInstance(), value);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getWrappedClass(), this.field.getName(),
"Field is not accessible", ex);
}
}
}
}
它的功能是直接操作Bean的属性值,而代替使用get/set方法去操作Bean。
它的实现原理就是简单的field.get(getWrappedInstance())和field.set(getWrappedInstance(), value)等。
它处理级联属性的大致步骤是:
- 遇上级联属性,先找出canonicalName
- 根据此canonicalName调用其field.get()拿到此字段的值~
- 若不为null(有初始值),那就继续解析此类型,循而往复即可~
PropertyAccessor使用Demo
本文以DirectFieldAccessor为例,介绍属性访问器PropertyAccessor的使用~
准备两个普通的JavaBean。苹果Apple:
代码语言:javascript复制@ToString
public class Apple {
private String color;
// 复杂类型
private Size size = new Size(); // 苹果的尺寸。 存在级联
private String[] arrStr = new String[1];
private List<String> listStr = new ArrayList<>();
private Map<Integer, String> map = new HashMap<>();
// 更为复杂的类型
private List<List<String>> listList = new ArrayList<>();
private List<Map<Integer, String>> listMap = new ArrayList<>();
public Apple() {
super();
listList.add(new ArrayList<>());
listMap.add(new HashMap<>());
}
}
尺寸Size:
代码语言:javascript复制@ToString
public class Size {
private Integer height;
private Integer width;
}
类Apple属性丰富,并且统一都没有提供get/set方法。使用DirectFieldAccessor直接的属性访问器给其赋值:
代码语言:javascript复制 public static void main(String[] args) {
Apple apple = new Apple();
PropertyAccessor accessor = new DirectFieldAccessor(apple);
// 设置普通属性
accessor.setPropertyValue("color", "红色");
// 设置嵌套属性(注意:此处能够正常work是因为有= new Size(),
// 否则报错:Value of nested property 'size' is null 下同~)
accessor.setPropertyValue("size.height", 10);
// 设置集合/数组属性
accessor.setPropertyValue("arrStr[0]", "arrStr");
accessor.setPropertyValue("arrStr[1]", "arrStr1"); // 注意:虽然初始化时初始化过数组了,但是仍以此处的为准
accessor.setPropertyValue("listStr[0]", "listStr");
accessor.setPropertyValue("listStr[0]", "listStr1"); // 如果角标index一样,后面覆盖前面的
// 虽然listStr是String的List,但是反射绕过了泛型 可以set进去,但一get就报错~~~需要注意这一点
//accessor.setPropertyValue("listStr[0]", new Size());
//accessor.setPropertyValue("listStr[1]", 20);
//System.out.println(apple.getListStr().get(0)); //Cannot convert value of type 'com.fsx.bean.Size' to required type 'java.lang.String'
// 设置Map:key只能是数值才行,否则是不好使的~~~~
//accessor.setPropertyValue("map['aaa']","myValue1"); //Caused by: java.lang.NumberFormatException: For input string: "aaa"
accessor.setPropertyValue("map[1]", "myValue2");
// 设置listList这种集合里的集合
accessor.setPropertyValue("listList[0][0]", "listList00");
accessor.setPropertyValue("listList[0][1]", "listList01");
//accessor.setPropertyValue("listList[1][0]","listList10"); //IndexOutOfBoundsException: Index: 1, Size: 1
//accessor.setPropertyValue("listList[1][1]","listList11"); //IndexOutOfBoundsException: Index: 1, Size: 1
// 设置listMap这种集合里面放Map
accessor.setPropertyValue("listMap[0][0]", "listMap00");
//accessor.setPropertyValue("listMap[0]['myKey']","listMapkey"); //For input string: "myKey"
// =========打印输出
System.out.println(apple); //Apple(color=红色, size=Size(height=10, width=null), arrStr=[arrStr, arrStr1], listStr=[listStr1], map={1=myValue2}, listList=[[listList00, listList01]], listMap=[{0=listMap00}])
}
从结果中是能够看出来的,使用DirectFieldAccessor能够正确完成属性赋值。这使用DirectFieldAccessor作为实现的话有几点使用小细节需要注意:
- 若是级联属性、集合数组等复杂属性,初始值不能为null
- 使用它给属性赋值无序提供get、set方法(侧面意思是:它不会走你的get/set方法逻辑)
当然若你希望null值能够被自动初始化也是可以的,请设值:accessor.setAutoGrowNestedPaths(true);这样数组、集合、Map等都会为null时候给你初始化(其它Bean请保证有默认构造函数)
在实际开发中,DirectFieldAccessor使用的场景相对较少,但有个典型应用是Spring-Data-Redis有使用DirectFieldAccessor来获取属性值~~~
若我们开发中只是单纯的想直接获取属性值,不妨可以使用它,形如这样:new DirectFieldAccessor(client).getPropertyValue(“redisURI”)非常的方便~~~
DirectFieldAccessor本质封装的还是jdk底层的反射获取属性的api
PropertyValue的作用什么?
Spring读源码系列番外篇—01–PropertyValue相关类
当设置属性值时,少不了两样东西:
- 属性访问表达式:如listMap0
- 属性值:
ProperyValue对象就是用来封装这些信息的。如果某个值要给赋值给bean属性,Spring都会把这个值包装成ProperyValue对象。
PropertyTokenHolder的作用是什么?
这个类的作用是对属性访问表达式的细化和归类。比如这句代码:
代码语言:javascript复制.setPropertyValue("listMap[0][0]", "listMapValue00");
这句代码的含义是:为Apple的成员变量listMap的第0个元素:即为Map。然后向该Map里放入键值对:0(key)和listMapValue00(value)。所以listMap0一个属性访问表达式,它在PropertyTokenHolder类里存储如下:
- canonicalName:listMap0:代表整个属性访问表达式
- actualName:listMap:仅包含最外层的属性名称
- keys:0, 0:数组的长度代表索引深度,各元素代表索引值
由于每个部分各有各的作用,所以就事先分解好,包装成对象,避免重复分解。
总结
本文介绍了PropertyAccessor属性访问器,并且以DirectFieldAccessor来直接操作Bean且提供了使用Demo。
通过本文的学习,能给你开辟一条新思路来操作JavaBean,而不仅仅只是通过get/set了.