25--Spring创建Bean的过程(七),bean属性填充转换属性值

版权声明:如有转载,请标明出处,谢谢合作! https://blog.csdn.net/lyc_liyanchao/article/details/82818662

上一小节分析了Spring填充bean属性时对bean属性值的解析过程,接下来就是解析完之后,对属性值的转换,例如 <entry key="age" value="3"/>中的value,应该是解析成String呢?还是解析成int呢?

我们接上一小节的分析,在前面我们提到过,当注入如List,Set属性,如果配置文件中配置了value-type属性,那么在解析值引用的同时会将集合中的值进行转换。其转换过程是循环集合属性并逐一转换。为了本章的分析,将上一小节xml配置文件中的value-type="java.lang.String"取消掉。

打开BeanWrapperImpl类的convertForProperty方法。

public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException {
    // CachedIntrospectionResults-->缓存了PropertyDescriptor集合
    CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
    // PropertyDescriptor-->表示JavaBean类通过存储器导出一个属性。主要方法:
    // 1. getReadMethod(),获得用于读取属性值的方法
    // 2. getWriteMethod(),获得用于写入属性值的方法
    PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
    if (pd == null) {
        throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,"No property '" + propertyName + "' found");
    }
    // 获取类型转换上下文
    TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
    if (td == null) {
        // 如果未能从缓存中获取,则创建一个新的TypeDescriptor并缓存
        td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
    }
    // 转换类型
    return convertForProperty(propertyName, null, value, td);
}

在该方法中执行了两个比较重要的操作,获取PropertyDescriptor对象和TypeDescriptor对象。

  • PropertyDescriptor -->表示了JavaBean类通过存储器导出一个属性,我们通过一张图片来了解一下其作用
    PropertyDescriptor

  • TypeDescriptor–>类型转换上下文,可以用来获取要转换的类型,例如前面提到的 <entry key="age" value="3"/>中的value,应该是解析成String呢?还是解析成int呢?通过TypeDescriptor就可以获取到要转换的具体类型。
    TypeDescriptor
    通过PropertyDescriptor对象,我们已经获取到了可读和可写方法,本例中是getName和setName方法,那么通过获取到的方法,就可以得到方法的返回值类型,该类型就是要转换的类型。
    来看td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));,先通过property(pd)方法得到Property对象,再通过得到的Property对象来创建TypeDescriptor对象,并缓存至cachedIntrospectionResults对象中。我们来分析下前两个步骤

Property对象表示了对JavaBean的描述,例如JavaBean的类型,名称,可读/可写方法,注解等等。我们来看其创建过程

private Property property(PropertyDescriptor pd) {
    GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
    // 获取javaBean的类型,可读方法,可写方法,及名称,并创建新的Property对象
    return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName());
}

TypeDescriptor对象表示了类型转换上线文,表示了要从哪个类型进行转换或者转换到哪个类型。我们来看其创建过程

public TypeDescriptor(Property property) {
    Assert.notNull(property, "Property must not be null");
    this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
    this.type = this.resolvableType.resolve(property.getType());
    this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
}

这样我们就得到了了属性值要转换的类型。而具体的转换工作,则是委托给了TypeConverterDelegate对象来完成,我们来看转换的具体过程。打开TypeConverterDelegate类的convertIfNecessary方法

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?
    // ①判断有无自定义属性编辑器
    PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

    ConversionFailedException conversionAttemptEx = null;

    // No custom editor but custom ConversionService specified?
    // ②判断有无自定义ConversionService
    ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
    if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
        TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
        if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
            try {
                return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
            }
            catch (ConversionFailedException ex) {
                // fallback to default conversion logic below
                conversionAttemptEx = ex;
            }
        }
    }

    Object convertedValue = newValue;

    // Value not of required type?
    // ClassUtils.isAssignableValue(requiredType, convertedValue)-->判断requiredType和convertedValue的class,是否相同,
    // 相同返回->true;否则返回->false
    // ③ 如果有自定义属性编辑器或者通过解析出来的值类型与真实的值类型的class不同
    // 例如<property name="age" value="3"/>,我们需要将value转换成int时
    if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
        if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) {
            TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
            if (elementTypeDesc != null) {
                Class<?> elementType = elementTypeDesc.getType();
                if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
                    convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
                }
            }
        }
        if (editor == null) {
            editor = findDefaultEditor(requiredType);
        }
        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,不接上面的else if
            // 如果经过转换过的值是数组类型,且其长度只有1,那么只取其第0个作为最终转换值
            if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
                convertedValue = Array.get(convertedValue, 0);
                standardConversion = true;
            }
            // 如果类型是String,并且是java的基本数据类型或者包装类型
            // 包括 boolean, byte, char, short, int, long, float, double
            // 和 Boolean, Byte, Character, Short, Integer, Long, Float, Double
            // 那么直接调用toString()方法返回即可,注意convertedValue是Object类型,不是基本或包装类型,所以是可以调用toString()方法的
            if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
                // We can stringify any primitive value...
                return (T) convertedValue.toString();
            }
            // 如果转换值是String类的实例,但是我们又不能转换为解析出来的requiredType的实例
            // 例如枚举类型值的注入
            else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
                if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
                    try {
                        Constructor<T> strCtor = requiredType.getConstructor(String.class);
                        return BeanUtils.instantiateClass(strCtor, convertedValue);
                    }
                    // 删除logger信息
                    catch (NoSuchMethodException ex) {}
                    catch (Exception ex) {}
                }
                String trimmedValue = ((String) convertedValue).trim();
                if (requiredType.isEnum() && "".equals(trimmedValue)) {
                    // It's an empty enum identifier: reset the enum value to null.
                    return null;
                }
                convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
                standardConversion = true;
            }
            // 数值类型
            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();
            }
        }

        // ⑤ 判定requiredType是否可从convertedValue转换而来,并尝试使用conversionService转换,及处理转换异常
        if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
            if (conversionAttemptEx != null) {
                // Original exception from former ConversionService call above...
                throw conversionAttemptEx;
            }
            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);
                }
            }

            // 到此为止,可以确定类型不匹配,无法转换,抛出IllegalArgumentException/IllegalStateException
            StringBuilder msg = new StringBuilder();
            msg.append("错误提示让我删了...");
            // 删除了msg提示配置
            if (editor != null) {
                throw new IllegalArgumentException(msg.toString());
            }
            else {
                throw new IllegalStateException(msg.toString());
            }
        }
    }

    if (conversionAttemptEx != null) {
        if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
            throw conversionAttemptEx;
        }
    }

    // ⑥返回转换值
    return (T) convertedValue;
}

该方法比较生涩难懂感觉,但是转换步骤大致可分为以下几步:

  • ①判断有无自定义属性编辑器
  • ②判断有无自定义ConversionService
  • ③ 如果有自定义属性编辑器或者通过解析出来的值类型与真实的值类型的class不同,则直接进行转换
  • ④执行转换主流程
  • ⑤ 判定requiredType是否可从convertedValue转换而来,并尝试使用conversionService转换,及处理转换异常
  • ⑥返回转换值
    其中⑤是最关键最重要的步骤,我们重点分析该步骤
1.转化数组

Spring对集合、数组类型的处理,都是循环并再次调用转换方法,从而将其具体元素转换为需要的值。

private Object convertToTypedArray(Object input, @Nullable String propertyName, Class<?> componentType) {
	// 如果是Collection实例,则将其转换为数组
	if (input instanceof Collection) {
		// Convert Collection elements to array elements.
		Collection<?> coll = (Collection<?>) input;
		Object result = Array.newInstance(componentType, coll.size());
		int i = 0;
		for (Iterator<?> it = coll.iterator(); it.hasNext(); i++) {
			Object value = convertIfNecessary(buildIndexedPropertyName(propertyName, i), null, it.next(), componentType);
			Array.set(result, i, value);
		}
		return result;
	}
	// 如果是数组
	else if (input.getClass().isArray()) {
		// Convert array elements, if necessary.
		if (componentType.equals(input.getClass().getComponentType()) && !this.propertyEditorRegistry.hasCustomEditorForElement(componentType, propertyName)) {
			return input;
		}
		int arrayLength = Array.getLength(input);
		Object result = Array.newInstance(componentType, arrayLength);
		for (int i = 0; i < arrayLength; i++) {
			Object value = convertIfNecessary(buildIndexedPropertyName(propertyName, i), null, Array.get(input, i), componentType);
			Array.set(result, i, value);
		}
		return result;
	}
	// 单个数组
	else {
		// A plain value: convert it to an array with a single component.
		Object result = Array.newInstance(componentType, 1);
		Object value = convertIfNecessary(buildIndexedPropertyName(propertyName, 0), null, input, componentType);
		Array.set(result, 0, value);
		return result;
	}
}
2.转换集合
private Collection<?> convertToTypedCollection(Collection<?> original, @Nullable String propertyName,
		Class<?> requiredType, @Nullable TypeDescriptor typeDescriptor) {

	if (!Collection.class.isAssignableFrom(requiredType)) {
		return original;
	}

	boolean approximable = CollectionFactory.isApproximableCollectionType(requiredType);
	if (!approximable && !canCreateCopy(requiredType)) {
		return original;
	}

	boolean originalAllowed = requiredType.isInstance(original);
	TypeDescriptor elementType = (typeDescriptor != null ? typeDescriptor.getElementTypeDescriptor() : null);
	if (elementType == null && originalAllowed && !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
		return original;
	}

	Iterator<?> it;
	try {
		it = original.iterator();
	}
	catch (Throwable ex) {
		return original;
	}

	Collection<Object> convertedCopy;
	try {
		if (approximable) {
			convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
		}
		else {
			convertedCopy = (Collection<Object>)ReflectionUtils.accessibleConstructor(requiredType).newInstance();
		}
	}
	catch (Throwable ex) {
		return original;
	}

	int i = 0;
	for (; it.hasNext(); i++) {
		Object element = it.next();
		String indexedPropertyName = buildIndexedPropertyName(propertyName, i);
		Object convertedElement = convertIfNecessary(indexedPropertyName, null, element,
				(elementType != null ? elementType.getType() : null) , elementType);
		try {
			convertedCopy.add(convertedElement);
		}
		catch (Throwable ex) {
			return original;
		}
		originalAllowed = originalAllowed && (element == convertedElement);
	}
	return (originalAllowed ? original : convertedCopy);
}
3.转换map
private Map<?, ?> convertToTypedMap(Map<?, ?> original, @Nullable String propertyName,
		Class<?> requiredType, @Nullable TypeDescriptor typeDescriptor) {

	if (!Map.class.isAssignableFrom(requiredType)) {
		return original;
	}

	boolean approximable = CollectionFactory.isApproximableMapType(requiredType);
	if (!approximable && !canCreateCopy(requiredType)) {
		return original;
	}

	boolean originalAllowed = requiredType.isInstance(original);
	TypeDescriptor keyType = (typeDescriptor != null ? typeDescriptor.getMapKeyTypeDescriptor() : null);
	TypeDescriptor valueType = (typeDescriptor != null ? typeDescriptor.getMapValueTypeDescriptor() : null);
	if (keyType == null && valueType == null && originalAllowed && !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
		return original;
	}

	Iterator<?> it;
	try {
		it = original.entrySet().iterator();
	}
	catch (Throwable ex) {
		return original;
	}

	Map<Object, Object> convertedCopy;
	try {
		if (approximable) {
			convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
		}
		else {
			convertedCopy = (Map<Object, Object>)
					ReflectionUtils.accessibleConstructor(requiredType).newInstance();
		}
	}
	catch (Throwable ex) {
		return original;
	}

	while (it.hasNext()) {
		Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next();
		Object key = entry.getKey();
		Object value = entry.getValue();
		String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
		Object convertedKey = convertIfNecessary(keyedPropertyName, null, key,
				(keyType != null ? keyType.getType() : null), keyType);
		Object convertedValue = convertIfNecessary(keyedPropertyName, null, value,
				(valueType!= null ? valueType.getType() : null), valueType);
		try {
			convertedCopy.put(convertedKey, convertedValue);
		}
		catch (Throwable ex) {
			return original;
		}
		originalAllowed = originalAllowed && (key == convertedKey) && (value == convertedValue);
	}
	return (originalAllowed ? original : convertedCopy);
}

其他如枚举、Number等类型的转换,已经在主方法里标注,大家可以自己debug跟踪看看具体的转换过程,如果经历convertIfNecessary方法之后,没有对值进行转换的话,那么默认返回String类型。

猜你喜欢

转载自blog.csdn.net/lyc_liyanchao/article/details/82818662