当前位置: 移动技术网 > IT编程>开发语言>Java > Spring源码解析之-TypeConverter、TypeConverterDelegate分析

Spring源码解析之-TypeConverter、TypeConverterDelegate分析

2020年07月17日  | 移动技术网IT编程  | 我要评论

一、介绍

TypeConverter 类主要是 负责类型转换,其实现类是 TypeConverterSupport,但是所有的具体实现都是在TypeConverterDelegate 里面完成的.

二、源码分析

TypeConverterDelegate 里面总共包含的方法如下:
在这里插入图片描述
这里主要分析一些主要的方法

2.1 convertIfNecessary 方法解析

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

		// 根据requiredType 和 propertyName 获取对应的定制编辑器
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// 获取对应的conversionService
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
		if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
		    // 为newValue 创建一个类型描述器
			TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			// 判断是否能sourceTypeDesc 转换为需要的typeDescriptor,如果可以
			//直接调用conversionService.convert 进行返回
			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;
		// 自定义editor 不为空 或者 对应的值 不是 需要的类型
		if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
		// 如果需要的类型是集合类型,并且值是String 类型(String⇒ 集合)
			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)) {
					    //将String字符串逗号分隔开,转换成字符串数组
						convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
					}
				}
			}
			//如果自定义编辑器 为null,就根据requiredType 设置相对应的编辑器
			if (editor == null) {
				editor = findDefaultEditor(requiredType);
			}
			//对convertedValue 进行相关的转换
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		}

		boolean standardConversion = false;

		if (requiredType != null) {
            // 这里都是一些标准的类型转换 ,根据各种类型调用相应的方法
			if (convertedValue != null) {
			// 如果是Object类型,直接强制转换并返回
				if (Object.class == requiredType) {
					return (T) convertedValue;
				}
				else if (requiredType.isArray()) {
					//如果需要的类型是枚举类型
					if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
					// 先将 转换为 逗号分隔的String 数组
						convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
					}
					// 转换为数组
					return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
				}
				else if (convertedValue instanceof Collection) {
					//如果convertedValue 是集合类型 ,进行相关的转换
					convertedValue = convertToTypedCollection(
							(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				}
				else if (convertedValue instanceof Map) {
					// 如果确定,将键和值转换为相应的目标类型
					convertedValue = convertToTypedMap(
							(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				}
				// 如果convertedValue 是数组类型,并且 长度为1 ,那就把get(0) 赋值给本身
				if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
					convertedValue = Array.get(convertedValue, 0);
					standardConversion = true;
				}
				// 如果需要的类型是 String ,并且convertedValue 的类型是基本类型或者装箱类型,那就直接toString 后强行转换
				if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
					// We can stringify any primitive value...
					return (T) convertedValue.toString();
				}
				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);
						}
						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;
				}
				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();
				}
			}

			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);
					}
				}

				// 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;
	}

2.2 findDefaultEditor 方法解析

	private PropertyEditor findDefaultEditor(@Nullable Class<?> requiredType) {
		PropertyEditor editor = null;
		if (requiredType != null) {
			// No custom editor -> check BeanWrapperImpl's default editors.
			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;
	}

2.3 doConvertValue 方法解析

private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
			@Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {

		Object convertedValue = newValue;
        // 如果编辑器不为null ,并且 转换值的类型不是 String
		if (editor != null && !(convertedValue instanceof String)) {
			// 调用setValue 方法
			//如果使用标准的PropertyEditors 的话,那就返回的是完全一样的对象,
			// 这里是调用专门的编辑器的setValue方法进行从非String 转到需要的类型上
			try {
				editor.setValue(convertedValue);
				Object newConvertedValue = editor.getValue();
				// 如果不一样,就说明进行了转换,需要将convertedValue 替换为转换后的值
				if (newConvertedValue != convertedValue) {
					convertedValue = newConvertedValue;
					// 这里将editor 置空,editor 已经进行了正确的转换,不需要再将其用于setAsText 的调用
					editor = null;
				}
			}
			catch (Exception ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
				}
				// 这里没有抛出异常,而是继续运行下面的代码
			}
		}
		Object returnValue = convertedValue;
         // 如果convertedValue  是String[] 数组类型,而需要的类型不是数组类型
         // 那就先将convertedValue 转换为 逗号分隔的String 值
		if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
			if (logger.isTraceEnabled()) {
				logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
			}
			// 将 String 数组 转换为 逗号分隔的String 值
			convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
		}
		if (convertedValue instanceof String) {
			if (editor != null) {
				if (logger.isTraceEnabled()) {
					logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
				}
				String newTextValue = (String) convertedValue;
				// 调用 PropertyEditor 的setAsText
				return doConvertTextValue(oldValue, newTextValue, editor);
			}
			// 如果requiredType 是String ,直接赋值并返回
			else if (String.class == requiredType) {
				returnValue = convertedValue;
			}
		}
		return returnValue;
	}

2.3 convertToTypedArray方法解析

private Object convertToTypedArray(Object input, @Nullable String propertyName, Class<?> componentType) {
        // 如果input 是集合类型
		if (input instanceof Collection) {
			// 将集合元素转换为数组元素
			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()) {
			// 对数组里面的元素进行 逐个转换(可能类型一样就不要转换)
			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 {
			//输入input 既不是集合类型,也不是数组类型,但是要转为数组
			// 就整个转化为一个元素的数组
			Object result = Array.newInstance(componentType, 1);
			Object value = convertIfNecessary(
					buildIndexedPropertyName(propertyName, 0), null, input, componentType);
			Array.set(result, 0, value);
			return result;
		}
	}

2.3 convertToTypedCollection方法解析

private Collection<?> convertToTypedCollection(Collection<?> original, @Nullable String propertyName,
			Class<?> requiredType, @Nullable TypeDescriptor typeDescriptor) {
        // 如果requiredType 不是集合类型,直接返回
		if (!Collection.class.isAssignableFrom(requiredType)) {
			return original;
		}
        // 判断是否是 集合相近的,比如:List,set,ArrayList..
		boolean approximable = CollectionFactory.isApproximableCollectionType(requiredType);
		//不是集合相近的,并且也不能对requiredType复制-注入原始Collection
		if (!approximable && !canCreateCopy(requiredType)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Custom Collection type [" + original.getClass().getName() +
						"] does not allow for creating a copy - injecting original Collection as-is");
			}
			return original;
		}

		boolean originalAllowed = requiredType.isInstance(original);
		TypeDescriptor elementType = (typeDescriptor != null ? typeDescriptor.getElementTypeDescriptor() : null);
		// 集合里面没有指定类型,并且original 就是 requiredType 类型,propertyEditorRegistry里面也没有对应的自定义编辑器,就直接返回
		if (elementType == null && originalAllowed &&
				!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
			return original;
		}

		Iterator<?> it;
		try {
			it = original.iterator();
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Cannot access Collection of type [" + original.getClass().getName() +
						"] - injecting original Collection as-is: " + 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) {
			if (logger.isDebugEnabled()) {
				logger.debug("Cannot create copy of Collection type [" + original.getClass().getName() +
						"] - injecting original Collection as-is: " + 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) {
				if (logger.isDebugEnabled()) {
					logger.debug("Collection type [" + original.getClass().getName() +
							"] seems to be read-only - injecting original Collection as-is: " + ex);
				}
				return original;
			}
			originalAllowed = originalAllowed && (element == convertedElement);
		}
		return (originalAllowed ? original : convertedCopy);
	}
	// 不是接口、不是抽象类、public 类型、有对应的构造方法
	private boolean canCreateCopy(Class<?> requiredType) {
		return (!requiredType.isInterface() && !Modifier.isAbstract(requiredType.getModifiers()) &&
				Modifier.isPublic(requiredType.getModifiers()) && ClassUtils.hasConstructor(requiredType));
	}

三、小结

这里面的方法都是类型转换相关的.

本文地址:https://blog.csdn.net/mamamalululu00000000/article/details/107304965

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网