Spring MVC类型转换器匹配
之前一直以为类型转换器是通过遍历的方式挨个匹配,直到匹配到合适的类型。但是一想到有这么多类型,如果挨个匹配不仅效率慢,而且还容易出错,翻翻源码,看到了类型转换的转换规则如下。
既然是在参数位置进行的转换,必然得从DispatcherServlet去入手:
所以在1040行打断点:
继续深入:
在这里获取到了ModelAndView结果,所以在这里执行完了我们在Controller层,对应@RequestMapping的方法,继续深入:
在里面会有一堆的set,不用管往下找到反射的部分:
这里注意840行,这里维护了一个缓存对象,包含了我们要用的转换器:
本地方法,, 在这里进行了 处理:
这里获取到了返回值,继续深入:
到这里才算真正找到了处理类型转换的地方
下面开始粘贴代码补充:
os: 构建springmvc源码失败,,真难受。
在这里获取到了参数的类型、参数名称等信息,下面必然就是对参数的处理,继续往下:
这里把参数对象的值进行转换并存储了起来,点进去继续深入:
既然返回值是我们要的结果,那
return
那行便是我们需要继续深入的:
这里就直接上代码,因为代码部分比较多所以这里只保留核心部分
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取参数名、是否是必须的、默认值等
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
// 当前参数类型信息、参数名等信息
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 获取到参数的名称
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
......
// 获取我们给参数传的值 未进行转换之前的值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
// 判断参数值是否为null
if (arg == null) {
......
}
// 判断参数值是否为空
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
......
}
// binderFactory中包含conversionService ,其中有124个类型转换器
if (binderFactory != null) {
......
try {
// 这里进行真正的数据转换,将我们传入的数据通过类型转换器转换为和参数对应的类型
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
.......
}
.......
}
继续深入,最终来到
TypeConverterDelegate.java --> convertIfNecessary ()
:
这里便是最终转换的地方,这个方法很庞大,我这里只保留我代码经过的地方
// 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) {
// 我们传入参数的类型
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
// newValue 我们传入的值
// sourceTypeDesc 原类型
// typeDescriptor 要转换成的类型
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
......
}
}
......
这里已经转换完毕了,我们可以继续点进去深入看一下:
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
...... 一些数据的校验
// 这里找到对应的转换器
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
// 这里获取到转换后的数值
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
GenericConverter converter = getConverter(sourceType, targetType);
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
// 将原类型和要转换的类型封装为 ConverterCacheKey
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
// 这里是维护了一个map ,里面存着 ConverterCacheKey-> GenericConverter 的数据
// this.converterCache
// private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
......
}
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
public Boolean convert(String source) {
String value = source.trim();
if (value.isEmpty()) {
return null;
}
value = value.toLowerCase();
if (trueValues.contains(value)) {
return Boolean.TRUE;
}
else if (falseValues.contains(value)) {
return Boolean.FALSE;
}
else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}