參考:【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);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/260075.html
標籤:其他
上一篇:vue-cli 中實作回應式布局
