源碼導(dǎo)讀:深入理解SpringMVC報(bào)400時(shí)的流程

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

網(wǎng)圖

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

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


springboot demo

這里什么都不傳鸳兽,就傳一個(gè)userId并指明是一個(gè)int類(lèi)型


postman

在postman將userId賦值為6e待讳,然后看他的報(bào)錯(cuò)流程。
第一步常規(guī)操作蛮原,用戶(hù)請(qǐng)求首先去到SpringMVC的DispatcherServlet類(lèi)里面的doDispatch方法
doDispatch

用戶(hù)現(xiàn)在拿到了HandlerAdapter卧须,正準(zhǔn)備去拿Resolver解析器。這時(shí)會(huì)跳到HandlerMethodArgumentResolverComposite類(lèi)的resolveArgument方法里面


HandlerMethodArgumentResolverComposite.resolveArgument

這里我補(bǔ)充一下儒陨,參數(shù)前的注解如:@RequestPart @RequestParam @RequestBody @ModelAttribute ... 每個(gè)注解有不同的resolver解析器花嘶,例如@RequestPart的參數(shù), 就會(huì)調(diào)用RequestPartMethodArgumentResolver這個(gè)class去解析參數(shù)蹦漠,如果參數(shù)前面沒(méi)有注解椭员,這時(shí)springMVC 會(huì)默認(rèn)為@RequestParam,并且跳到RequestParamMethodArgumentResolver這個(gè)解析器津辩。

RequestParamMethodArgumentResolver中的resolveName方法

這里會(huì)看到首先判斷請(qǐng)求進(jìn)來(lái)的是不是MultipartFile , 如果不是就會(huì)直接以String格式拿到參數(shù)拆撼。 所以為什么如果要上傳文件, controller的file一定要選MultipartFile喘沿,不然就會(huì)以String形式送到下面的邏輯代碼闸度,最后match不到而報(bào)錯(cuò)。
ok蚜印, 拿到用戶(hù)錯(cuò)誤的‘6e’的userId后莺禁,請(qǐng)求就會(huì)跳到AbstractNamedValueMethodArgumentResolver的resolveArgument方法:


AbstractNamedValueMethodArgumentResolver

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


GenericConversionService的covert

拿到StringToNumberConverter的覆蓋器哟冬,最后在StringToNumberConverterFactory這個(gè)工廠(chǎng)類(lèi)里:
StringToNumberConverterFactory

用NumberUtils去parseNumber這個(gè)不是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報(bào)400流程

至此忆绰,我們都大概了解了springMVC的報(bào)錯(cuò)流程浩峡。這時(shí)我們玩深入點(diǎn):



我們依然用回@RequestParam,這次我們傳實(shí)體UserEntity


UserEntity

UserEntity里面再包一個(gè)PetEntity的實(shí)體错敢,準(zhǔn)備好了json的string:
  {   "id": 0,   "petEntity": {     "id": 19,     "name": "string" ,   "sex": 6   },   "name": "doggie" }

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



當(dāng)?shù)搅薚ypeConverterDelegate的大概148行左右



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

然后不斷往外拋平斩,直至拋到用戶(hù)手上:

{
    "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"
 }

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

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



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



會(huì)跳到AbstractMessageConverterMethodArgumentResolver侈沪,這個(gè)class里面有個(gè)messageConverters的list:

    List<HttpMessageConverter<?>> messageConverters;

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

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

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



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

總結(jié):其實(shí)springMVC從用戶(hù)request進(jìn)來(lái)再解析成controller需要的parameter參數(shù)扣墩,并沒(méi)有那么玄乎,不同annotation注釋對(duì)應(yīng)不同的Resolver解析器扛吞, 我們甚至可以寫(xiě)一個(gè)自己的解析器呻惕,再調(diào)用一下第三方的插件,例如阿里的fastJson去完成項(xiàng)目需要的功能滥比。

SpringMVC是個(gè)龐大的項(xiàng)目亚脆,如有讀者覺(jué)得上述實(shí)驗(yàn)有所欠缺或不對(duì),歡迎前來(lái)斧正盲泛!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末濒持,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寺滚,更是在濱河造成了極大的恐慌柑营,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件村视,死亡現(xiàn)場(chǎng)離奇詭異官套,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蚁孔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)奶赔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人杠氢,你說(shuō)我怎么就攤上這事站刑。” “怎么了鼻百?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵笛钝,是天一觀(guān)的道長(zhǎng)质况。 經(jīng)常有香客問(wèn)我,道長(zhǎng)玻靡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任中贝,我火速辦了婚禮囤捻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邻寿。我一直安慰自己蝎土,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布绣否。 她就那樣靜靜地躺著誊涯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒜撮。 梳的紋絲不亂的頭發(fā)上暴构,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音段磨,去河邊找鬼取逾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛苹支,可吹牛的內(nèi)容都是我干的砾隅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼债蜜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼晴埂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起寻定,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤儒洛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后特姐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晶丘,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年唐含,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浅浮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捷枯,死狀恐怖滚秩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淮捆,我是刑警寧澤郁油,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布本股,位于F島的核電站,受9級(jí)特大地震影響桐腌,放射性物質(zhì)發(fā)生泄漏拄显。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一案站、第九天 我趴在偏房一處隱蔽的房頂上張望躬审。 院中可真熱鬧,春花似錦蟆盐、人聲如沸承边。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)博助。三九已至,卻和暖如春痹愚,著一層夾襖步出監(jiān)牢的瞬間富岳,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工里伯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留城瞎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓疾瓮,卻偏偏與公主長(zhǎng)得像脖镀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狼电,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容