背景
項目中使用LocalDateTime系列作為dto中時間的類型,但是spring收到參數後總報錯,為了全局配置時間類型轉換,嘗試瞭如下3中方法。
注:本文基於Springboot2.0測試,如果無法生效可能是spring版本較低導致的。PS:如果你的Controller中的LocalDate類型的參數啥註解(RequestParam、PathVariable等)都沒加,也是會出錯的,因為默認情況下,解析這種參數使用ModelAttributeMethodProcessor進行處理,而這個處理器要通過反射實例化一個對象出來,然後再對對象中的各個參數進行convert,但是LocalDate類沒有構造函數,無法反射實例化因此會報錯!!!
當LocalDateTime作為RequestParam或者PathVariable時
這種情況要和時間作為Json字符串時區別對待,因為前端json轉後端pojo底層使用的是Json序列化Jackson工具(HttpMessgeConverter);而時間字符串作為普通請求參數傳入時,轉換用的是Converter,兩者有區別哦。
在這種情況下,有如下幾種方案:
1. 使用Converter
以上兩個bean會注入到spring mvc的參數解析器(好像叫做ParameterConversionService),當傳入的字符串要轉為LocalDateTime類時,spring會調用該Converter對這個入參進行轉換。
2. 使用ControllerAdvice配合initBinder
從名字就可以看出來,這是在controller做環切(這裡面還可以全局異常捕獲),在參數進入handler之前進行轉換;轉換為我們相應的對象。
當LocalDateTime作為Json形式傳入
這種情況下,如同上文描述,要利用Jackson的json序列化和反序列化來做:
來個完整的配置吧
深入研究SpringMVC數據綁定過程
接下來進入debug模式,看看mvc是如何將我們request中的參數綁定到我們controller層方法入參的:
寫一個簡單controller,下個斷點看看方法調用棧:
斷住以後,我們看下方法調用棧中一些關鍵方法:
根據上述分析,發現invokeForRequest:142, InvocableHandlerMethod這裡的代碼是用來拿到實際參數的:
進入這個方法看看是什麼操作:
進入resolveArgument看看:
這裡根據參數獲取相應的參數解析器,看看內部如何獲取的:
這裡,遍歷參數解析器,查找有沒有適合的解析器!那麼,有哪些參數解析器呢(我測試的時候有26個)???我列出幾個重要的看看,是不是很眼熟!!!
我們進入最常用的一個解析器看看他的supportsParameter方法,發現就是通過參數註解來獲取相應的解析器的。
也就是說,對於@RequestParam和@RequestBody以及@PathVariable註解的參數,SpringMVC會使用不通的參數解析器進行數據綁定!
那麼,這三種解析器分別使用什麼Converter解析參數呢?我們分別進入三種解析器看一看:
首先看下RequestParamMethodArgumentResolver發現內部使用WebDataBinder進行數據綁定,底層使用的是ConversionService (也就是我們的Converter注入的地方)
然後看下RequestResponseBodyMethodProcessor發現使用的轉換器是HttpMessageConverter類型的:
最後看下PathVariableMethodArgumentResolver發現 和RequestParam走的執行路徑一致(二者都是繼承自AbstractNamedValueMethodArgumentResolver解析器),因此代碼就不貼了。
總結
如果要轉換request傳來的參數到我們指定的類型,根據入參註解要進行區分:
- 如果是RequestBody,那麼通過配置ObjectMapper(這個玩意兒會注入到Jackson的HttpMessagConverter裡面,即MappingJackson2HttpMessageConverter中)來實現Json格式數據的序列化和反序列化;
- 如果是RequestParam或者PathVariable類型的參數,通過配置Converter實現參數轉換(這些Converter會注入到ConversionService中)。
作者:TinyThing
來源:簡書
商業用途請與原作者聯繫,本文只做展示分享,不妥侵刪!
閱讀更多 程序猿猩球 的文章