網路上對這個問題的分析及解決不是很深入,大部分並不能解決問題,而且內容基本相同,拿來主義,把內容放在自己的部落格上!
報錯原因可能有兩種情況:
1.請求頭中沒有設定Content-Type引數,或Content-Type引數值不是application/json;
2.請求頭中正確設定了Content-Type引數及引數值,但是在專案jar依賴中(pom.xml或build.gradle)沒有新增處理json字串的處理類,如果SpringMVC框架在啟動的時候,檢查com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator有一個不存在或不能載入,則不會註冊MappingJackson2HttpMessageConverter,這個類使用Jackson將json請求引數轉成相應的方法引數;同樣檢查com.google.gson.Gson,如果不存在或不能載入,則不會註冊GsonHttpMessageConverter,這個類使用Gson將json請求引數轉成相應的方法引數;如果依賴的Jackson和Gson都沒有被新增或不能載入,則SpringMVC將找不到對應的引數處理類。
原始碼分析
在使用SpringMVC的時候,都會新增
![](https://i.iter01.com/images/d64fb604d0dd78cc5fd251b31278eb8711e152a63d7e06b92bbf6257c430d9f1.png)
![](https://i.iter01.com/images/04a07a268ef5ec05082da2a71e11a65921cdac58b217db865d949d0cc77559da.png)
![](https://i.iter01.com/images/adbfdee882296ce5269a9cfe68cbc04a0053fab4235571befb7a7b89eda127dc.png)
![](https://i.iter01.com/images/7e34ac6c6c58075f430a49765ed3708645fe5fac4fc930e95c31a7db95354d50.png)
![](https://i.iter01.com/images/82b99b2787f3ed10990b17af0286719096518b4150117239df7805a7ef63aa35.png)
如果配置了json引數轉換處理類,SpringMVC框架將根據請求頭中的Content-Type引數遍歷messageConverters,選擇匹配的轉換器類,進行引數轉換。如果Content-Type引數值型別是messageConverters中不支援的,那麼就沒辦法做轉換。
總結
首先,SpringMVC框架在啟動的時候會遍歷Spring容器中的所有bean,對標註了@Controller或@RequestMapping註解的類中方法進行遍歷,將類和方法上的@RequestMapping註解值進行合併,使用@RequestMapping註解的相關引數值(如value、method等)封裝一個RequestMappingInfo,將這個Controller例項、方法及方法引數資訊(型別、註解等)封裝到HandlerMethod中,然後以RequestMappingInfo為key,HandlerMethod為value存到一個以Map為結構的handlerMethods中。
接著,將@RequestMapping註解中的value(即請求路徑)值取出,即url,然後以url為key,以RequestMappingInfo為value,存到一個以Map為結構的urlMap屬性中。
客戶端發起請求的時候,根據請求的URL到urlMap中查詢,找到RequestMappingInfo,然後根據RequestMappingInfo到handlerMethods中查詢,找到對應的HandlerMethod,接著將HandlerMethod封裝到HandlerExecutionChain;接著遍歷容器中所有HandlerAdapter實現類,找到支援這次請求的HandlerAdapter,如RequestMappingHandlerAdapter,然後執行SpringMVC攔截器的前置方法(preHandle方法),然後對請求引數解析及轉換,這裡主要根據HandlerMethod中封裝的引數資訊(方法引數上的註解)來遍歷argumentResolvers(List結構,儲存了HandlerMethodArgumentResolver介面實現類,不同實現類,實現對不同註解引數的解析,如RequestResponseBodyMethodProcessor可以實現對@RequestBody和@ResponseBody引數的解析),找到支援這個註解的HandlerMethodArgumentResolver實現類,然後解析請求引數。
插播一下請求引數的解析及轉換,下圖是HandlerMethodArgumentResolver介面的實現類。從上圖中可以看到很多常見註解引數的解析類,這裡分析RequestResponseBodyMethodProcessor,其它處理類感興趣的可以自己研究一下。RequestResponseBodyMethodProcessor會從請求頭中獲取Content-Type引數值,例如application/json,然後遍歷messageConverters,查詢能夠處理這種Content-Type的轉換器類,如果messageConverters中有可以處理application/json請求的處理類,如Jackson或Gson,則使用Jackson或Gson對請求體中的引數進行讀取轉換,轉換成具體方法引數型別,下面是Jackson具體的處理程式碼。
如果messageConverters沒有匹配的處理類,那就會報415。
最後,(使用反射)呼叫具體Controller的對應方法返回一個ModelAndView物件,執行攔截器的後置方法(postHandle方法),然後對返回的結果進行處理,最後執行afterCompletion方法。