源碼導讀:深入理解SpringMVC報400時的流程

相信很多同學都了解過(或者面試前都會復(fù)習過)springMVC的執(zhí)行流程,如下圖:

網(wǎng)圖

轉(zhuǎn)載請注明出處:Michael孟良

這里我想細節(jié)地理解下springMVC報400(也就是上圖第5步拿到HandlerAdapter后,前往handler時出的錯)的執(zhí)行流程,希望對讀者之后的工作或面試有幫助。
我們舉個簡單的例子(因時間關(guān)系, 我用springboot的2.1.3.RELEASE版本作為這個springMVC的實驗載體):


springboot demo

這里什么都不傳,就傳一個userId并指明是一個int類型


postman

在postman將userId賦值為6e,然后看他的報錯流程。
第一步常規(guī)操作,用戶請求首先去到SpringMVC的DispatcherServlet類里面的doDispatch方法
doDispatch

用戶現(xiàn)在拿到了HandlerAdapter,正準備去拿Resolver解析器。這時會跳到HandlerMethodArgumentResolverComposite類的resolveArgument方法里面


HandlerMethodArgumentResolverComposite.resolveArgument

這里我補充一下,參數(shù)前的注解如:@RequestPart @RequestParam @RequestBody @ModelAttribute ... 每個注解有不同的resolver解析器,例如@RequestPart的參數(shù), 就會調(diào)用RequestPartMethodArgumentResolver這個class去解析參數(shù),如果參數(shù)前面沒有注解,這時springMVC 會默認為@RequestParam,并且跳到RequestParamMethodArgumentResolver這個解析器。

RequestParamMethodArgumentResolver中的resolveName方法

這里會看到首先判斷請求進來的是不是MultipartFile , 如果不是就會直接以String格式拿到參數(shù)。 所以為什么如果要上傳文件, controller的file一定要選MultipartFile,不然就會以String形式送到下面的邏輯代碼,最后match不到而報錯。
ok, 拿到用戶錯誤的‘6e’的userId后,請求就會跳到AbstractNamedValueMethodArgumentResolver的resolveArgument方法:


AbstractNamedValueMethodArgumentResolver

這里到最關(guān)鍵的一步了:
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
這里arg是‘6e’ (不是int類型的參數(shù)), parameter.getParameterType()為int,左跳右跳,跳到GenericConversionService的covert


GenericConversionService的covert

拿到StringToNumberConverter的覆蓋器,最后在StringToNumberConverterFactory這個工廠類里:
StringToNumberConverterFactory

用NumberUtils去parseNumber這個不是int的‘6e’,最后爆400:
{
  "timestamp": "2019-05-05T04:26:05.337+0000",
  "status": 400,
  "error": "Bad Request",
  "message": "Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is     java.lang.NumberFormatException: For input string: \"6e\"",
  "path": "/hello"
}

這里總結(jié)一下:


String轉(zhuǎn)int報400流程

至此,我們都大概了解了springMVC的報錯流程。這時我們玩深入點:



我們依然用回@RequestParam,這次我們傳實體UserEntity


UserEntity

UserEntity里面再包一個PetEntity的實體,準備好了json的string:
  {   "id": 0,   "petEntity": {     "id": 19,     "name": "string" ,   "sex": 6   },   "name": "doggie" }

然后我們讀它的流程。
它的流程大致上和上面string轉(zhuǎn)int例子一樣,但去到拿ConverterFactory時,而繼續(xù)往下走:



當?shù)搅薚ypeConverterDelegate的大概148行左右



但找不到對應(yīng)的editor和轉(zhuǎn)換策略時, 就會:
throw new IllegalStateException(msg.toString());

然后不斷往外拋,直至拋到用戶手上:

{
    "timestamp": "2019-05-05T05:40:20.581+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'com.example.demo.UserEentity'; nested     exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type     'com.example.demo.UserEentity': no matching editors or conversion strategy found",
    "path": "/hello"
 }

這里其實我覺得應(yīng)該是一個400的bad request . 但springMVC定義成了500。

到這里我有疑惑了,那像其他@RequestBdoy @RequestPart ..是怎么將String轉(zhuǎn)實體的呢?帶著疑惑我又做了個實驗:



這次用@RequestPart 做實驗,發(fā)現(xiàn)在去到RequestPartMethodArgumentResolver時



會跳到AbstractMessageConverterMethodArgumentResolver,這個class里面有個messageConverters的list:

    List<HttpMessageConverter<?>> messageConverters;

這里就包含12個converter, 最后一個就是FastJsonHttpMessageConverter , 經(jīng)過兩層if if 過濾后得到當前converter為FastJsonHttpMessageConverter ,當?shù)搅?/p>

    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                            ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));

springMVC 就會將參數(shù)交給第三方插件,也就是將FastJsonHttpMessageConverter 賦給了genericConverter,進行后續(xù)操作:



到這里可以看出,最后會交由阿里插件fastjson去完成String 轉(zhuǎn) 實體對象。

總結(jié):其實springMVC從用戶request進來再解析成controller需要的parameter參數(shù),并沒有那么玄乎,不同annotation注釋對應(yīng)不同的Resolver解析器, 我們甚至可以寫一個自己的解析器,再調(diào)用一下第三方的插件,例如阿里的fastJson去完成項目需要的功能。

SpringMVC是個龐大的項目,如有讀者覺得上述實驗有所欠缺或不對,歡迎前來斧正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容