參數解析
說到參數解析,springmvc中處理參數的是HandlerMethodArgumentResolver接口
public?interface?HandlerMethodArgumentResolver?{
???//?判斷是否支持該類型參數
???boolean?supportsParameter(MethodParameter?parameter);
???//?進行參數解析
???Object?resolveArgument(MethodParameter?parameter,?ModelAndViewContainer?mavContainer,
?????????NativeWebRequest?webRequest,?WebDataBinderFactory?binderFactory)?throws?Exception;
}
其有一個抽象實現類AbstractNamedValueMethodArgumentResolver,有很多有用的子類
-
MapMethodProcessor
這個用來處理 Map/ModelMap 類型的參數,解析完成后返回 model。
-
PathVariableMethodArgumentResolver
這個用來處理使用了 @PathVariable
注解并且參數類型不為 Map 的參數,參數類型為 Map 則使用 PathVariableMapMethodArgumentResolver
來處理。
-
PathVariableMapMethodArgumentResolver
見上。
-
ErrorsMethodArgumentResolver
這個用來處理 Error 參數,例如我們做參數校驗時的 BindingResult。
-
AbstractNamedValueMethodArgumentResolver
這個用來處理 key/value 類型的參數,如請求頭參數、使用了
@PathVariable
注解的參數以及 Cookie 等。 -
RequestHeaderMethodArgumentResolver
這個用來處理使用了 @RequestHeader
注解,并且參數類型不是 Map 的參數(參數類型是 Map 的使用 RequestHeaderMapMethodArgumentResolver
)。
-
RequestHeaderMapMethodArgumentResolver
見上。
-
RequestAttributeMethodArgumentResolver
這個用來處理使用了 @RequestAttribute
注解的參數。
-
RequestParamMethodArgumentResolver
這個功能就比較廣了。使用了 @RequestParam
注解的參數、文件上傳的類型 MultipartFile、或者一些沒有使用任何注解的基本類型(Long、Integer)以及 String 等,都使用該參數解析器處理。需要注意的是,如果 @RequestParam
注解的參數類型是 Map,則該注解必須有 name 值,否則解析將由 RequestParamMapMethodArgumentResolver
完成。
-
RequestParamMapMethodArgumentResolver
見上。
-
AbstractCookieValueMethodArgumentResolver
這個是一個父類,處理使用了 @CookieValue
注解的參數。
-
ServletCookieValueMethodArgumentResolver
這個處理使用了 @CookieValue
注解的參數。
-
MatrixVariableMethodArgumentResolver
這個處理使用了 @MatrixVariable
注解并且參數類型不是 Map 的參數,如果參數類型是 Map,則使用 MatrixVariableMapMethodArgumentResolver
來處理。
-
MatrixVariableMapMethodArgumentResolver
見上。
-
SessionAttributeMethodArgumentResolver
這個用來處理使用了 @SessionAttribute
注解的參數。
-
ExpressionValueMethodArgumentResolver
這個用來處理使用了 @Value
注解的參數。
-
ServletResponseMethodArgumentResolver
這個用來處理 ServletResponse、OutputStream 以及 Writer 類型的參數。
-
ModelMethodProcessor
這個用來處理 Model 類型參數,并返回 model。
-
ModelAttributeMethodProcessor
這個用來處理使用了 @ModelAttribute
注解的參數。
-
SessionStatusMethodArgumentResolver
這個用來處理 SessionStatus 類型的參數。
-
PrincipalMethodArgumentResolver
這個用來處理 Principal 類型參數
-
AbstractMessageConverterMethodArgumentResolver
這是一個父類,當使用 HttpMessageConverter 解析 requestbody 類型參數時,相關的處理類都會繼承自它。
-
RequestPartMethodArgumentResolver
這個用來處理使用了 @RequestPart
注解、MultipartFile 以及 Part 類型的參數。
-
RequestResponseBodyMethodProcessor
這個用來處理添加了 @RequestBody
注解的參數。
-
HttpEntityMethodProcessor
這個用來處理 HttpEntity 和 RequestEntity 類型的參數。
-
ServletWebArgumentResolverAdapter
這個給父類提供 request。
-
UriComponentsBuilderMethodArgumentResolver
這個用來處理 UriComponentsBuilder 類型的參數。
-
ServletRequestMethodArgumentResolver
這個用來處理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 類型的參數。
-
HandlerMethodArgumentResolverComposite
這個看名字就知道是一個組合解析器,它是一個代理,具體代理其他干活的那些參數解析器。
-
RedirectAttributesMethodArgumentResolver
這個用來處理 RedirectAttributes 類型的參數
這些解析器是在執行
//?Actually?invoke?the?handler.
mv?=?ha.handle(processedRequest,?response,?mappedHandler.getHandler());
執行對應的Controller方法前來進行參數解析時調用的org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
if?(this.argumentResolvers.supportsParameter(parameter))?{
???try?{
??????args[i]?=?this.argumentResolvers.resolveArgument(
????????????parameter,?mavContainer,?request,?this.dataBinderFactory);
??????continue;
???}
???catch?(Exception?ex)?{
??????if?(logger.isDebugEnabled())?{
?????????logger.debug(getArgumentResolutionErrorMessage("Failed?to?resolve",?i),?ex);
??????}
??????throw?ex;
???}
}
RequestResponseBodyMethodProcessor調用消息解析器
我之前只知道解析@RequestBody需要使用消息解析器HttpMessageConverter,但是沒有深究是從哪調用的。突然看到消息解析我才知道原來是RequestResponseBodyMethodProcessor調用的。org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument來進行參數解析
遍歷消息解析器找到可以進行解析該類型的消息解析器
for?(HttpMessageConverter<?>?converter?:?this.messageConverters)?{
對于簡單的參數會以簡單的轉換器進行轉換,而這些簡單的轉換器是Spring MVC自身已經提供了的,但是如果是轉換HTTP請求體,會調用HttpMessageConverter接口的方法對請求體的信息進行轉換
public?interface?HttpMessageConverter<T>?{
???
??//?是否可讀,clazz為java類型,mediaType為HTTP請求類型
???boolean?canRead(Class<?>?clazz,?MediaType?mediaType);
???
??//?判斷clazz類型是否能夠轉換為mediaType媒體類型,其中clazz為java類型,mediaType為HTTP響應類型
???boolean?canWrite(Class<?>?clazz,?MediaType?mediaType);
???
??//?可支持的媒體類型列表
???List<MediaType>?getSupportedMediaTypes();
???
??//?當canRead驗證通過后,讀入HTTP請求信息
???T?read(Class<??extends?T>?clazz,?HttpInputMessage?inputMessage)
?????????throws?IOException,?HttpMessageNotReadableException;
???
??//?單canWrite方法驗證通過后,寫入響應
???void?write(T?t,?MediaType?contentType,?HttpOutputMessage?outputMessage)
?????????throws?IOException,?HttpMessageNotWritableException;
}
HttpMessageConverter接口是將HTTP請求體轉換為對應的java對象,對于HTTP參數和其他內容需要使用參數轉換規則
參數轉換
Spring MVC中,是通過WebDataBinder機制來獲取參數的,它的主要作用是解析HTTP請求的上下文,然后在控制器的調用之前轉換參數并且提供驗證的功能,為調用控制器方法做準備。處理器會從HTTP請求中讀取數據,然后通過三種接口進行各類參數轉換(Converter、Formatter、GenericConverter)。
Converter接口
Converter接口是一個普通的轉換器
public?interface?Converter<S,?T>?{
?
?T?convert(S?source);
}
可以將某個類型轉換為另一個類型
Formatter接口
Formatter接口是一個格式化轉換器,如將日期字符串格式化
public?interface?Formatter<T>?extends?Printer<T>,?Parser<T>?{
}
GenericConverter接口
GenericConverter接口是將HTTP參數轉換為數組
public?interface?GenericConverter?{
???
???Set<ConvertiblePair>?getConvertibleTypes();
???
???Object?convert(Object?source,?TypeDescriptor?sourceType,?TypeDescriptor?targetType);
???/**
????*?Holder?for?a?source-to-target?class?pair.
????*/
???final?class?ConvertiblePair?{
??????private?final?Class<?>?sourceType;
??????private?final?Class<?>?targetType;
??????/**
???????*?Create?a?new?source-to-target?pair.
???????*?@param?sourceType?the?source?type
???????*?@param?targetType?the?target?type
???????*/
??????public?ConvertiblePair(Class<?>?sourceType,?Class<?>?targetType)?{
?????????Assert.notNull(sourceType,?"Source?type?must?not?be?null");
?????????Assert.notNull(targetType,?"Target?type?must?not?be?null");
?????????this.sourceType?=?sourceType;
?????????this.targetType?=?targetType;
??????}
??????public?Class<?>?getSourceType()?{
?????????return?this.sourceType;
??????}
??????public?Class<?>?getTargetType()?{
?????????return?this.targetType;
??????}
??????@Override
??????public?boolean?equals(Object?other)?{
?????????if?(this?==?other)?{
????????????return?true;
?????????}
?????????if?(other?==?null?||?other.getClass()?!=?ConvertiblePair.class)?{
????????????return?false;
?????????}
?????????ConvertiblePair?otherPair?=?(ConvertiblePair)?other;
?????????return?(this.sourceType?==?otherPair.sourceType?&&?this.targetType?==?otherPair.targetType);
??????}
??????@Override
??????public?int?hashCode()?{
?????????return?(this.sourceType.hashCode()?*?31?+?this.targetType.hashCode());
??????}
??????@Override
??????public?String?toString()?{
?????????return?(this.sourceType.getName()?+?"?->?"?+?this.targetType.getName());
??????}
???}
}
ConversionService接口
public?interface?ConversionService?{
???boolean?canConvert(Class<?>?sourceType,?Class<?>?targetType);
???boolean?canConvert(TypeDescriptor?sourceType,?TypeDescriptor?targetType);
???<T>?T?convert(Object?source,?Class<T>?targetType);
???Object?convert(Object?source,?TypeDescriptor?sourceType,?TypeDescriptor?targetType);
}
數據驗證
Validator接口用于數據驗證
public?interface?Validator?{
??//?判斷當前驗證器是否支持該Class類型的驗證
???boolean?supports(Class<?>?clazz);
???
??//?如果supports返回true,則執行該方法驗證邏輯
???void?validate(Object?target,?Errors?errors);
}
WebDataBinder還可以進行驗證,使用@InitBinder注解可以允許在進入控制器方法之前修改WebDataBinder機制,可以來設置驗證器
通過WebDataBinder#setValidator來添加驗證器
https://zhhll.icu/2023/框架/springmvc/底層剖析/6.參數解析/
本文由 mdnice 多平臺發布