目录
- 一、介绍
- 二、源码分析
- 2.1 convertIfNecessary 方法解析
- 2.2 findDefaultEditor 方法解析
- 2.3 doConvertValue 方法解析
- 2.3 convertToTypedArray方法解析
- 2.3 convertToTypedCollection方法解析
- 三、小结
一、介绍
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;// 获取对应的conversionServiceConversionService 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 belowconversionAttemptEx = 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 lookupif (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 == nullif (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/IllegalStateExceptionStringBuilder 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 ,并且 转换值的类型不是 Stringif (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 的setAsTextreturn 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复制-注入原始Collectionif (!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));}
三、小结
这里面的方法都是类型转换相关的.