天天看點

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

前言

SpringMVC是目前主流的Web MVC架構之一。

現象

本文使用的demo基于maven,是根據入門blog的例子繼續寫下去的。

我們先來看一看對應的現象。我們這裡的配置檔案 *-dispatcher.xml中的關鍵配置如下(其他正常的配置檔案不在講解,可參考本文一開始提到的入門blog):

(視圖配置省略)

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

pom中需要有以下依賴(Spring依賴及其他依賴不顯示):

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

這個依賴是json序列化的依賴。

ok。我們在Controller中添加一個method:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

直接通路位址:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

我們看到,短短幾行配置。使用@ResponseBody注解之後,Controller傳回的對象 自動被轉換成對應的json資料,在這裡不得不感歎SpringMVC的強大。

我們好像也沒看到具體的配置,唯一看到的就是*-dispatcher.xml中的一句配置:。其實就是這個配置,導緻了java對象自動轉換成json對象的現象。

那麼spring到底是如何實作java對象到json對象的自動轉換的呢?為什麼轉換成了json資料,如果想轉換成xml資料,那該怎麼辦?

源碼分析

本文使用的spring版本是4.0.2。

在講解這個配置之前,我們先了解下Spring的消息轉換機制。@ResponseBody這個注解就是使用消息轉換機制,最終通過json的轉換器轉換成json資料的。

HttpMessageConverter接口就是Spring提供的http消息轉換接口。有關這方面的知識大家可以參考"參考資料"中的第二條連結,裡面講的很清楚。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

下面開始分析這句配置:

這句代碼在spring中的解析類是:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

在AnnotationDrivenBeanDefinitionParser源碼的152行parse方法中:

分别執行個體化了RequestMappingHandlerMapping,ConfigurableWebBindingInitializer,RequestMappingHandlerAdapter等諸多類。

其中RequestMappingHandlerMapping和RequestMappingHandlerAdapter這兩個類比較重要。

RequestMappingHandlerMapping處理請求映射的,處理@RequestMapping跟請求位址之間的關系。

RequestMappingHandlerAdapter是請求處理的擴充卡,也就是請求之後處理具體邏輯的執行,關系到哪個類的哪個方法以及轉換器等工作,這個類是我們講的重點,其中它的屬性messageConverters是本文要講的重點。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

私有方法:getMessageConverters

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

從代碼中我們可以,RequestMappingHandlerAdapter設定messageConverters的邏輯:

1.如果節點有子節點message-converters,那麼它的轉換器屬性messageConverters也由這些子節點組成。

message-converters的子節點配置如下:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

2.message-converters子節點不存在或它的屬性register-defaults為true的話,加入其他的轉換器:ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter等。

我們看到這麼一段:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

這些boolean屬性是哪裡來的呢,它們是AnnotationDrivenBeanDefinitionParser的靜态變量。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

看到這裡,讀者應該明白了為什麼本文一開始在pom檔案中需要加入對應的jackson依賴,為了讓json轉換器jackson成為預設轉換器之一。

的作用讀者也明白了。

下面我們看如何通過消息轉換器将java對象進行轉換的。

RequestMappingHandlerAdapter在進行handle的時候,會委托給HandlerMethod(具體由子類ServletInvocableHandlerMethod處理)的invokeAndHandle方法進行處理,這個方法又轉接給HandlerMethodReturnValueHandlerComposite處理。

HandlerMethodReturnValueHandlerComposite維護了一個HandlerMethodReturnValueHandler清單。

HandlerMethodReturnValueHandler是一個對傳回值進行處理的政策接口,這個接口非常重要。然後找到對應的HandlerMethodReturnValueHandler對結果值進行處理。

最終找到RequestResponseBodyMethodProcessor這個Handler(由于使用了@ResponseBody注解)。

RequestResponseBodyMethodProcessor的supportsReturnType方法:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

具體的轉換方法:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

至于為何是請求頭部的Accept資料,讀者可以進去debug這個getAcceptableMediaTypes方法看看。我就不羅嗦了~~~

ok。至此,我們走遍了所有的流程。

現在,回過頭來看。為什麼一開始的demo輸出了json資料?

我們來分析吧。

由于我們隻配置了,是以使用spring預設的那些轉換器。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

很明顯,我們看到了2個xml和1個json轉換器。 要看能不能轉換,得看HttpMessageConverter接口的public boolean canWrite(Class<?> clazz, MediaType mediaType)方法是否傳回true來決定的。

我們先分析SourceHttpMessageConverter:

它的canWrite方法被父類AbstractHttpMessageConverter重寫了。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

發現SUPPORTED_CLASSES中沒有Map類(本文demo傳回的是Map類),是以不支援。

下面看Jaxb2RootElementHttpMessageConverter:

這個類直接重寫了canWrite方法。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

需要有XmlRootElement注解。很明顯,Map類當然沒有。

最終MappingJackson2HttpMessageConverter比對,進行json轉換。(為何比對,請讀者自行檢視源碼)

執行個體講解

我們分析了轉換器的轉換過程之後,下面就通過執行個體來驗證我們的結論吧。

首先,我們先把xml轉換器實作。

之前已經分析,預設的轉換器中是支援xml的。下面我們加上注解試試吧。

由于Map是jdk源碼中的部分,是以我們用Employee來做demo。

是以,Controller加上一個方法:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

實體中加上@XmlRootElement注解

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

我們發現,解析成了xml。

這裡為什麼解析成xml,而不解析成json呢?

之前分析過,消息轉換器是根據class和mediaType決定的。

我們使用firebug看到:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

我們發現Accept有xml,沒有json。是以解析成xml了。

我們再來驗證,同一位址,HTTP頭部不同Accept。看是否正确。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?
為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

驗證成功。

關于配置

如果不想使用中預設的RequestMappingHandlerAdapter的話,我們可以在重新定義這個bean,spring會覆寫掉預設的RequestMappingHandlerAdapter。

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  <property name="messageConverters">
    <list>
      <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
      <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
      <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
    </list>
  </property>
</bean>           

或者如果隻想換messageConverters的話。

<mvc:annotation-driven>
  <mvc:message-converters>
    <bean class="org.example.MyHttpMessageConverter"/>
    <bean class="org.example.MyOtherHttpMessageConverter"/>
  </mvc:message-converters>
</mvc:annotation-driven>           

如果還想用其他converters的話。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

以上是spring-mvc jar包中的converters。

這裡我們使用轉換xml的MarshallingHttpMessageConverter。

這個converter裡面使用了marshaller進行轉換

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

我們這裡使用XStreamMarshaller。

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

json沒有轉換器,傳回406.

至于xml格式的問題,大家自行解決吧。這裡用的是XStream~。

使用這種方式,pom别忘記了加入xstream的依賴:

為什麼Spring MVC能自動轉換json/xml,你研究過它背後的原理嗎?

總結

寫了這麼多,可能讀者覺得有點羅嗦。畢竟這也是自己的一些心得,希望都能說出來與讀者共享。

剛接觸SpringMVC的時候,發現這種自動轉換機制很牛逼,但是一直沒有研究它的原理,目前,算是了了一個小小心願吧,SpringMVC還有很多内容,以後自己研究其他内容的時候還會與大家一起共享的。

文章難免會出現一些錯誤,希望讀者們能指明出來。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/zhibo

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-03-29

本文作者:網際網路架構師

本文來自:“

網際網路架構師 微信公衆号

”,了解相關資訊可以關注“

網際網路架構師