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

2022-05-10 16:36:45 浏览数 (1)

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

  • 引子
  • PropertyAccessor
  • TypeConverter
    • ConfigurablePropertyAccessor
    • TypeConverterSupport
      • TypeConverterDelegate
      • AbstractPropertyAccessor
        • AbstractNestablePropertyAccessor
          • DirectFieldAccessor
  • 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了.

0 人点赞