小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

SpringMVC關(guān)于json、xml自動(dòng)轉(zhuǎn)換的原理研究[附帶源碼分析] – format...

 旭龍 2014-08-17

前言

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

如果有同學(xué)對(duì)它不熟悉,那么請(qǐng)參考它的入門(mén)blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html

文章有點(diǎn)略長(zhǎng),請(qǐng)讀者耐心地看下去。

現(xiàn)象

本文使用的demo基于maven,是根據(jù)入門(mén)blog的例子繼續(xù)寫(xiě)下去的。

我們先來(lái)看一看對(duì)應(yīng)的現(xiàn)象。 我們這里的配置文件 *-dispatcher.xml中的關(guān)鍵配置如下(其他常規(guī)的配置文件不在講解,可參考本文一開(kāi)始提到的入門(mén)blog):

<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>

這個(gè)依賴分別是json序列化的依賴。

ok。我們?cè)贑ontroller中添加一個(gè)method:

@RequestMapping(“/xmlOrJson”)
@ResponseBody
public Map<String, Object> xmlOrJson() {
Map
<String, Object> map = new HashMap<String, Object>();
map.put(
“l(fā)ist”, employeeService.list());
return map;
}

直接訪問(wèn)地址:

我們看到,短短幾行配置。使用@ResponseBody注解之后,Controller返回的對(duì)象 自動(dòng)被轉(zhuǎn)換成對(duì)應(yīng)的json數(shù)據(jù),在這里不得不感嘆SpringMVC的強(qiáng)大。

我們好像也沒(méi)看到具體的配置,唯一看到的就是*-dispatcher.xml中的一句配置:<mvc:annotation-driven/>。其實(shí)就是這個(gè)配置,導(dǎo)致了java對(duì)象自動(dòng)轉(zhuǎn)換成json對(duì)象的現(xiàn)象。

那么spring到底是如何實(shí)現(xiàn)java對(duì)象到j(luò)son對(duì)象的自動(dòng)轉(zhuǎn)換的呢? 為什么轉(zhuǎn)換成了json數(shù)據(jù),如果想轉(zhuǎn)換成xml數(shù)據(jù),那該怎么辦?

源碼分析

本文使用的spring版本是4.0.2。  

在講解<mvc:annotation-driven/>這個(gè)配置之前,我們先了解下Spring的消息轉(zhuǎn)換機(jī)制。@ResponseBody這個(gè)注解就是使用消息轉(zhuǎn)換機(jī)制,最終通過(guò)json的轉(zhuǎn)換器轉(zhuǎn)換成json數(shù)據(jù)的。

HttpMessageConverter接口就是Spring提供的http消息轉(zhuǎn)換接口。有關(guān)這方面的知識(shí)大家可以參考”參考資料”中的第二條鏈接,里面講的很清楚。

下面開(kāi)始分析<mvc:annotation-driven/>這句配置:

這句代碼在spring中的解析類(lèi)是:

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

分別實(shí)例化了RequestMappingHandlerMapping,ConfigurableWebBindingInitializer,RequestMappingHandlerAdapter等諸多類(lèi)。

其中RequestMappingHandlerMappingRequestMappingHandlerAdapter這兩個(gè)類(lèi)比較重要。

RequestMappingHandlerMapping處理請(qǐng)求映射的,處理@RequestMapping跟請(qǐng)求地址之間的關(guān)系。

RequestMappingHandlerAdapter是請(qǐng)求處理的適配器,也就是請(qǐng)求之后處理具體邏輯的執(zhí)行,關(guān)系到哪個(gè)類(lèi)的哪個(gè)方法以及轉(zhuǎn)換器等工作,這個(gè)類(lèi)是我們講的重點(diǎn),其中它的屬性messageConverters是本文要講的重點(diǎn)。

私有方法:getMessageConverters

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

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

message-converters的子節(jié)點(diǎn)配置如下:

<mvc:annotation-driven>
<mvc:message-converters register-defaults=”true”>
<bean class=”org.example.MyHttpMessageConverter”/>
<bean class=”org.example.MyOtherHttpMessageConverter”/>
</mvc:message-converters>
</mvc:annotation-driven>

 

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

我們看到這么一段:

這些boolean屬性是哪里來(lái)的呢,它們是AnnotationDrivenBeanDefinitionParser的靜態(tài)變量。

 其中ClassUtils中的isPresent方法如下:

看到這里,讀者應(yīng)該明白了為什么本文一開(kāi)始在pom文件中需要加入對(duì)應(yīng)的jackson依賴,為了讓json轉(zhuǎn)換器jackson成為默認(rèn)轉(zhuǎn)換器之一。

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

下面我們看如何通過(guò)消息轉(zhuǎn)換器將java對(duì)象進(jìn)行轉(zhuǎn)換的。

 

RequestMappingHandlerAdapter在進(jìn)行handle的時(shí)候,會(huì)委托給HandlerMethod(具體由子類(lèi)ServletInvocableHandlerMethod處理)的invokeAndHandle方法進(jìn)行處理,這個(gè)方法又轉(zhuǎn)接給HandlerMethodReturnValueHandlerComposite處理。

HandlerMethodReturnValueHandlerComposite維護(hù)了一個(gè)HandlerMethodReturnValueHandler列表。HandlerMethodReturnValueHandler是一個(gè)對(duì)返回值進(jìn)行處理的策略接口。然后找到確定的HandlerMethodReturnValueHandler對(duì)結(jié)果值進(jìn)行處理。

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

RequestResponseBodyMethodProcessor的supportsReturnType方法:

然后使用handleReturnValue方法進(jìn)行處理:

我們看到,這里使用了轉(zhuǎn)換器?! ?/p>

具體的轉(zhuǎn)換方法:

至于為何是請(qǐng)求頭部的Accept數(shù)據(jù),讀者可以進(jìn)去debug這個(gè)getAcceptableMediaTypes方法看看。 我就不羅嗦了~~~

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

 

現(xiàn)在,回過(guò)頭來(lái)看。為什么一開(kāi)始的demo輸出了json數(shù)據(jù)?

我們來(lái)分析吧。

 

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

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

我們先分析SourceHttpMessageConverter:

它的canWrite方法被父類(lèi)AbstractHttpMessageConverter重寫(xiě)了。

發(fā)現(xiàn)SUPPORTED_CLASSES中沒(méi)有Map類(lèi)(本文demo返回的是Map類(lèi)),因此不支持。

下面看Jaxb2RootElementHttpMessageConverter:

這個(gè)類(lèi)直接重寫(xiě)了canWrite方法。

需要有XmlRootElement注解。 很明顯,Map類(lèi)當(dāng)然沒(méi)有。

最終MappingJackson2HttpMessageConverter匹配,進(jìn)行json轉(zhuǎn)換。(為何匹配,請(qǐng)讀者自行查看源碼)

實(shí)例講解

 我們分析了轉(zhuǎn)換器的轉(zhuǎn)換過(guò)程之后,下面就通過(guò)實(shí)例來(lái)驗(yàn)證我們的結(jié)論吧。

首先,我們先把xml轉(zhuǎn)換器實(shí)現(xiàn)。

之前已經(jīng)分析,默認(rèn)的轉(zhuǎn)換器中是支持xml的。下面我們加上注解試試吧。

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

因此,Controller加上一個(gè)方法:

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

實(shí)體中加上@XmlRootElement注解

結(jié)果如下:

我們發(fā)現(xiàn),解析成了xml。

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

 

之前分析過(guò),消息轉(zhuǎn)換器是根據(jù)class和mediaType決定的。

我們使用firebug看到:

我們發(fā)現(xiàn)Accept有xml,沒(méi)有json。因此解析成xml了。

 

我們?cè)賮?lái)驗(yàn)證,同一地址,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”
}
});

驗(yàn)證成功。

關(guān)于配置

如果不想使用<mvc:annotation-driven/>中默認(rèn)的RequestMappingHandlerAdapter的話,我們可以在重新定義這個(gè)bean,spring會(huì)覆蓋掉默認(rèn)的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 register-defaults=”true”>
<bean class=”org.example.MyHttpMessageConverter”/>
<bean class=”org.example.MyOtherHttpMessageConverter”/>
</mvc:message-converters>
</mvc:annotation-driven>

如果還想用其他converters的話。

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

這里我們使用轉(zhuǎn)換xml的MarshallingHttpMessageConverter。

這個(gè)converter里面使用了marshaller進(jìn)行轉(zhuǎn)換

我們這里使用XStreamMarshaller?! ?/p>

json沒(méi)有轉(zhuǎn)換器,返回406.

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

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

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

總結(jié)

 寫(xiě)了這么多,可能讀者覺(jué)得有點(diǎn)羅嗦。 畢竟這也是自己的一些心得,希望都能說(shuō)出來(lái)與讀者共享。

剛接觸SpringMVC的時(shí)候,發(fā)現(xiàn)這種自動(dòng)轉(zhuǎn)換機(jī)制很牛逼,但是一直沒(méi)有研究它的原理,目前,算是了了一個(gè)小小心愿吧,SpringMVC還有很多內(nèi)容,以后自己研究其他內(nèi)容的時(shí)候還會(huì)與大家一起共享的。

文章難免會(huì)出現(xiàn)一些錯(cuò)誤,希望讀者們能指明出來(lái)。

參考資料

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

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

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

本文鏈接:SpringMVC關(guān)于json、xml自動(dòng)轉(zhuǎn)換的原理研究[附帶源碼分析],轉(zhuǎn)載請(qǐng)注明。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多