json報文轉化為xml報文,SpringMVC json/xml自動轉換

 2023-10-30 阅读 24 评论 0

摘要:為什么80%的碼農都做不了架構師?>>> ?? 前言 SpringMVC是目前主流的Web MVC框架之一。? json報文轉化為xml報文?如果有同學對它不熟悉,那么請參考它的入門blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 現象 本文

為什么80%的碼農都做不了架構師?>>> ??hot3.png

前言

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

json報文轉化為xml報文?如果有同學對它不熟悉,那么請參考它的入門blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html

現象

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

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

xmlparser、(視圖配置省略)

<mvc:resources location="/static/" mapping="/static/**"/>
<mvc:annotation-driven/>
<context:component-scan base-package="org.format.demo.controller"/>

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

<dependency><groupId>org.codehaus.jackson</groupId><artifactId>jackson-core-asl</artifactId><version>1.9.13</version>
</dependency>
<dependency><groupId>org.codehaus.jackson</groupId><artifactId>jackson-mapper-asl</artifactId><version>1.9.13</version>
</dependency>

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

java json轉字符串?ok。我們在Controller中添加一個method:

@RequestMapping("/xmlOrJson")
@ResponseBody
public Map<String, Object> xmlOrJson() {Map<String, Object> map = new HashMap<String, Object>();map.put("list", employeeService.list());return map;
}

直接訪問地址:

xml2json、我們看到,短短幾行配置。使用@ResponseBody注解之后,Controller返回的對象 自動被轉換成對應的json數據,在這里不得不感嘆SpringMVC的強大。

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

那么spring到底是如何實現java對象到json對象的自動轉換的呢? 為什么轉換成了json數據,如果想轉換成xml數據,那該怎么辦?

源碼分析

java string轉json?本文使用的spring版本是4.0.2。 ?

在講解<mvc:annotation-driven/>這個配置之前,我們先了解下Spring的消息轉換機制。@ResponseBody這個注解就是使用消息轉換機制,最終通過json的轉換器轉換成json數據的。

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

下面開始分析<mvc:annotation-driven/>這句配置:

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

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

分別實例化了RequestMappingHandlerMapping,ConfigurableWebBindingInitializer,RequestMappingHandlerAdapter等諸多類。

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

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

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

私有方法:getMessageConverters

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

1.如果<mvc:annotation-driven>節點有子節點message-converters,那么它的轉換器屬性messageConverters也由這些子節點組成。

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

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

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

我們看到這么一段:

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

?其中ClassUtils中的isPresent方法如下:

看到這里,讀者應該明白了為什么本文一開始在pom文件中需要加入對應的jackson依賴,為了讓json轉換器jackson成為默認轉換器之一。

<mvc:annotation-driven>的作用讀者也明白了。

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

?

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

HandlerMethodReturnValueHandlerComposite維護了一個HandlerMethodReturnValueHandler列表。HandlerMethodReturnValueHandler是一個對返回值進行處理的策略接口,這個接口非常重要。關于這個接口的細節,請參考樓主的另外一篇博客:http://www.cnblogs.com/fangjian0423/p/springMVC-request-param-analysis.html。然后找到對應的HandlerMethodReturnValueHandler對結果值進行處理。

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

RequestResponseBodyMethodProcessor的supportsReturnType方法:

然后使用handleReturnValue方法進行處理:

我們看到,這里使用了轉換器。  

具體的轉換方法:

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

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

現在,回過頭來看。為什么一開始的demo輸出了json數據?

我們來分析吧。

由于我們只配置了<mvc:annotation-driven>,因此使用spring默認的那些轉換器。

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

我們先分析SourceHttpMessageConverter:

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

發現SUPPORTED_CLASSES中沒有Map類(本文demo返回的是Map類),因此不支持。

下面看Jaxb2RootElementHttpMessageConverter:

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

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

最終MappingJackson2HttpMessageConverter匹配,進行json轉換。(為何匹配,請讀者自行查看源碼)

實例講解

?我們分析了轉換器的轉換過程之后,下面就通過實例來驗證我們的結論吧。

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

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

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

因此,Controller加上一個方法:

@RequestMapping("/xmlOrJsonSimple")
@ResponseBody
public Employee xmlOrJsonSimple() {return employeeService.getById(1);
}

實體中加上@XmlRootElement注解

結果如下:

我們發現,解析成了xml。

這里為什么解析成xml,而不解析成json呢?

?

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

我們使用firebug看到:

我們發現Accept有xml,沒有json。因此解析成xml了。

?

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

$.ajax({url: "${request.contextPath}/employee/xmlOrJsonSimple",success: function(res) {console.log(res);},headers: {"Accept": "application/xml"}
});

$.ajax({url: "${request.contextPath}/employee/xmlOrJsonSimple",success: function(res) {console.log(res);},headers: {"Accept": "application/json"}
});

驗證成功。

關于配置

如果不想使用<mvc:annotation-driven/>中默認的RequestMappingHandlerAdapter的話,我們可以在重新定義這個bean,spring會覆蓋掉默認的RequestMappingHandlerAdapter。

為何會覆蓋,請參考樓主的另外一篇博客:http://www.cnblogs.com/fangjian0423/p/spring-Ordered-interface.html

<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 jar包中的converters。

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

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

我們這里使用XStreamMarshaller。  

json沒有轉換器,返回406.

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

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

<dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.7</version>
</dependency>

總結

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

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

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

參考資料

http://my.oschina.net/HeliosFly/blog/205343

http://my.oschina.net/lichhao/blog/172562

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html

轉載于:https://my.oschina.net/oosc/blog/1787655

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/165155.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息