参考:【SpringMVC】浅谈Convert/Format机制与HttpMessageConverter的关系
HttpMessageConverter和Converter的区别
Spring3 引入了Convert/Format SPI
,Convert SPI
可以实现任意java类型的转换;Format SPI
支持国际化,并在前者的基础上实现了 String 与任意类型的转换。这两类 SPI 属于spring-core,被整个spring-framework共享,是一种通用的类型转换器。
HttpMessageConverter 属于spring-web,HttpMessageConverter
是将HTTP请求内容转换成java对象,以及将java对象转换成HTTP响应内容,说白了就是对HTTP的反序列化和序列化,它的仅仅被用于HTTP主体和java对象之间的转换。
SpringMVC 默认会注册一些自带的HttpMessageConvertor
(从先后顺序排列分别为ByteArrayHttpMessageConverter
、StringHttpMessageConverter
、ResourceHttpMessageConverter
、SourceHttpMessageConverter
、AllEncompassingFormHttpMessageConverter
)。
如果后端服务使用Restful API的形式,一般使用 JSON 作为前后端通信的格式规范,由于 SpringMVC 自带MappingJackson2HttpMessageConverter
,在依赖中引入 jackson 包后,容器会把MappingJackson2HttpMessageConverter
自动注册到 converter 链的末尾。
在HandlerAdapter
执行目标方法之前,需要调用resolveArgument()
依次解析出每个入参对象,然后才会执行目标方法。
在resolveArgument()
内部,首先调用getArgumentResolver()
获得能解析当前入参的HandlerMethodArgumentResolver
解析器,然后调用这个解析器的resolveArgument()
获得一个入参对象。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//根据参数类型和参数上的注解, 获得能解析这个参数的解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
//解析获得入参对象
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
getArgumentResolver()
会返回什么类型的HandlerMethodArgumentResolver
,这与入参类型和入参上的注解有关,Spring MVC默认注册了以下方法参数解析器:
都是字如其意的名称,比如PathVariableMethodArgumentResolver
用于解析 @PathVariable 标注的入参,RequestParamMethodArgumentResolver
用于解析 @RequestParam 标注的入参等。
关键的是:
有一些解析器是调用**binder.convertIfNecessary()
,使用绑定器的ConversionService内部适当的Converter进行类型转换,通常是字符串转入参对象(因为是超文本协议嘛),比如PathVariableMethodArgumentResolver
、RequestParamMethodArgumentResolver
**等。
有一些解析器是调用**readWithMessageConverters()
,使用适当的HttpMessageConverter将HTTP请求转换为入参对象,比如RequestResponseBodyMethodProcessor
、HttpEntityMethodProcessor
**等。
下面列举两个解析器的resolveArgument()
源码看看:
RequestParamMethodArgumentResolver
的resolveArgument()
方法:
首先获取@RequestParam注解指定的请求参数的值,然后交给数据绑定器内部的ConversionService转换成入参对象。
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
//获取@RequestParam注解指定的请求参数名称
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
//获取请求参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
//创建数据绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
//使用绑定器内部的ConversionService将这个请求参数转换成入参对象
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
//返回入参对象
return arg;
}
RequestResponseBodyMethodProcessor
的resolveArgument()
方法:
首先调用readWithMessageConverters() ,选取适当的HTTPMessageConverter将HTTP请求内容转换成入参对象,最后使用校验器校验,然后返回入参。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
//选取合适的HTTPMessageConverter将请求内容转换成入参对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
//创建数据绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
//如果入参标了@Valid或@Validated的话, 使用校验器进行校验
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
//返回入参对象
return adaptArgumentIfNecessary(arg, parameter);
}