<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringMVC系列 教程(五):關(guān)于json、xml自動(dòng)轉(zhuǎn)換的原理研究(附...

          共 4763字,需瀏覽 10分鐘

           ·

          2019-12-06 23:23

          c690b88b8725945ba78bf9b9da812acb.webp

          7961fd30452644ba2fd89f1ec58ac8de.webp

          來(lái)源:https://0x9.me/9T0Vj

          一.?前言


          SpringMVC是目前主流的Web MVC框架之一。?上一篇介紹了解了SpringMVC中Controller的方法中參數(shù)的工作原理,今天說(shuō)一說(shuō)關(guān)于json、xml自動(dòng)轉(zhuǎn)換的原理研究!

          二.?現(xiàn)象

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

          我們先來(lái)看一看對(duì)應(yīng)的現(xiàn)象。我們這里的配置文件 *-dispatcher.xml中的關(guān)鍵配置如下:

          (視圖配置省略)

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

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

          <dependency>  <groupId>org.codehaus.jacksongroupId>  <artifactId>jackson-core-aslartifactId>  <version>1.9.13version>dependency><dependency>  <groupId>org.codehaus.jacksongroupId>  <artifactId>jackson-mapper-aslartifactId>  <version>1.9.13version>dependency>

          這個(gè)依賴(lài)是json序列化的依賴(lài)。

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

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

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

          d99d866cad8bc614cb10ac8f4b448ec0.webp

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

          我們好像也沒(méi)看到具體的配置,唯一看到的就是*-dispatcher.xml中的一句配置:。其實(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。?

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

          HttpMessageConverter接口就是Spring提供的http消息轉(zhuǎn)換接口。

          0272ed83937a7cf6a3b5f97af343c332.webp

          下面開(kāi)始分析這句配置:

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

          c09a87641692f6879e65efe750cdc243.webp

          在A(yíng)nnotationDrivenBeanDefinitionParser源碼的152行parse方法中:

          分別實(shí)例化了

          RequestMappingHandlerMapping,ConfigurableWebBindingInitializer,

          RequestMappingHandlerAdapter等諸多類(lèi)。


          其中RequestMappingHandlerMapping和

          RequestMappingHandlerAdapter這兩個(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)。

          b8d10a38651f0319a9266d14115b1c06.webp

          私有方法:getMessageConverters

          f4b2506e258c18e43f704dbe9f979a29.webp

          從代碼中我們可以,RequestMappingHandlerAdapter

          設(shè)置messageConverters的邏輯:

          1.如果節(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>    <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的話(huà),加入其他的轉(zhuǎn)換器:

          ByteArrayHttpMessageConverter、

          StringHttpMessageConverter、

          ResourceHttpMessageConverter等。

          我們看到這么一段:

          484094153c055a269b3d44da288bf68b.webp

          這些boolean屬性是哪里來(lái)的呢,

          它們是AnnotationDrivenBeanDefinitionParser的靜態(tài)變量。

          fcbf6a1c0258b35a046db2588cd1d398.webp

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

          969fe5ff29c1a0cd0163b83f34087535.webp

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

          的作用讀者也明白了。

          下面我們看如何通過(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)行處理的策略接口,這個(gè)接口非常重要。關(guān)于這個(gè)接口的細(xì)節(jié),請(qǐng)參考樓主的另外一篇博客:http://www.cnblogs.com/fangjian0423/p/springMVC-request-param-analysis.html。然后找到對(duì)應(yīng)的HandlerMethodReturnValueHandler對(duì)結(jié)果值進(jìn)行處理。

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

          RequestResponseBodyMethodProcessor的supportsReturnType方法:

          d9421f00ee343c0411e790b1d947cbc8.webp

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

          6fbe6cf60fb3f788ee3528ca3e67a41a.webp

          我們看到,這里使用了轉(zhuǎn)換器。  

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

          42aa730e6e9391c410c50c26b39284b6.webp

          b41d48abf754f322d0982193d418080c.webp

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

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

          ?

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

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

          ?

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

          05a5698308691a72878e0ac9faf2d945.webp

          很明顯,我們看到了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ě)了。

          c403622c173ce2b734a45928f9f0574f.webp

          8359ebaa4c5cc28a76f473bd243eb7a5.webp

          2de7ef6adeab10c87dcadbedba52b70a.webp

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

          下面看Jaxb2RootElementHttpMessageConverter:

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

          aeff2792f0f665b370b1b590cc52e0e7.webp

          需要有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")@ResponseBodypublic Employee xmlOrJsonSimple() {    return employeeService.getById(1);}

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

          da0ad41e80a412438e238ad5ee3cb754.webp

          結(jié)果如下:

          27674c1bc562100c6dd5a2898e1e0ad1.webp

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

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

          ?

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

          我們使用firebug看到:

          00121ff37a5fa85669afd43a5bf0317d.webp

          我們發(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"    }});

          020363f485a950de76c5620c9a1a590d.webp

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

          7132cd98f187a307a2a4a10ca607a363.webp

          驗(yàn)證成功。

          五. 關(guān)于配置

          如果不想使用

          中默認(rèn)的RequestMappingHandlerAdapter的話(huà),

          我們可以在重新定義這個(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的話(huà)。

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

          如果還想用其他converters的話(huà)。

          8f522f98fd7a3104a5343550382a6686.webp

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

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

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

          6afa4f4fc06c877fca4220db3e45060d.webp

          我們這里使用XStreamMarshaller。  

          4ce8f814d944e493eeb8a10a02d7b77f.webp

          9a6e519cb480266d5728c0e68a782112.webp

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

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

          使用這種方式,pom別忘記了加入xstream的依賴(lài):

          <dependency>  <groupId>com.thoughtworks.xstreamgroupId>  <artifactId>xstreamartifactId>  <version>1.4.7version>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)。


          - End -

          8f81892c0d271b9545ebda44323eff89.webp

          術(shù)點(diǎn)轉(zhuǎn)


          面試題系列教程??點(diǎn)擊-->?面試題技術(shù)干貨連載目錄?跳轉(zhuǎn)


          Maven系列教程??點(diǎn)擊-->?Maven技術(shù)干貨連載目錄?跳轉(zhuǎn)


          MyBatis系列教程??點(diǎn)擊-->?MyBatis技術(shù)干貨連載目錄?跳轉(zhuǎn)


          JVM調(diào)優(yōu)總結(jié)系列教程??點(diǎn)擊-->?JVM調(diào)優(yōu)技術(shù)干貨連載目錄?跳轉(zhuǎn)





          點(diǎn),?17c283109358487c28fda36c977b4433.webp

          瀏覽 111
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  xxxxxbbbbb | 无码AV中文字幕 | 中国一级黄色毛片 | 91超碰人妻 | 亚洲免费专区 |