class TypeConverterDelegate {
// ...
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
*
* @param propertyName name of the property
* @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 typeDescriptor the descriptor for the target property or field
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@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 {
// 在当前的流程中propertyName、oldValue为null,newValue为前台传过来的真实参数值,requiredType为处理方法要求的类型,typeDescriptor为要求类型的描述封装类
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
// 上述条件成立
// 在现在的逻辑里sourceTypeDesc必然为String的TypeDescriptor
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
// 可以转换
// canConvert实际上是尝试获取符合条件的GenericConverter,如果有就说明可以转换
// 对于String -> Integer的转换,会先将String类型拆为 [String,Serializable,Comparable,CharSequence,Object]的类型层,Integer同样拆为自己的类型层,之后先后遍历每个类型来准确判断是否存在可以转换的转换器
try {
// 最终会调用到自定义的转换器
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
} catch (ConversionFailedException ex) {
// fallback to default conversion logic below
// 转换失败,暂存异常,将会执行默认的转换逻辑
conversionAttemptEx = ex;
}
}
}
// 因为spring自带了很多常见类型的转换器,大部分都可以通过上面的转换器完成。
// 程序运行到这里没有结束的话很可能说明类型是没有定义转换器的自定义类型或者参数格式真的不正确
Object convertedValue = newValue;
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
// 最后的条件为 当newValue不是requiredType的实例
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
// isAssignableFrom用来判断Collection是否为requiredType的父类或者接口,或者二者是否为同一类型或接口
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
// 相当于convertedValue.split(",")
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
// 使用默认的editor进行转换,不过默认的editor的转换有可能与期望的不一致。(比如 "1,2,3,4" -> ArrayList<String>{"1,2,3,4"},结果是只有一个元素的list)
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
// 加下来会根据requiredType来做出相应的转换
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (Object.class == requiredType) {
// requiredType是Object
return (T) convertedValue;
} else if (requiredType.isArray()) {
// requiredType是数组
// 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) {
// 将convertedValue转换为集合,内部对每个元素调用了convertIfNecessary(即本方法)
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
} else if (convertedValue instanceof Map) {
// 将convertedValue转换为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;
}
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;
}
// ...
}
最后
看源码虽然很费时间,但是的确是能学到很多东西的,而且也能发现很多以前不知道的事情(比如RequestParam注解的name和defaultName参数是可以嵌套引用配置文件中的内容,也可以写SPEL表达式),但其中还是有一些地方不是很清楚。
虽说现在项目都直接使用JSON做前后端交互,大部分类型转换的任务都交给了JSON序列化框架,但是参数绑定这里还是值得看一看,等到需要用的时候就可以直接拿出来用。