Spring RestTemplate源碼學(xué)習(xí) ——梳理內(nèi)部實(shí)現(xiàn)過程

本文系轉(zhuǎn)載文章,轉(zhuǎn)自博客園:安靜的下雪天 ?http://www.cnblogs.com/quiet-snowy-day/p/6228198.html

本篇概要

RestTemplate 類圖

postForEntity 方法處理過程

requestCallback.doWithRequest 方法處理過程

responseExtractor.extractData 方法處理過程

關(guān)于GenericHttpMessageConverter

關(guān)于RestTemplate 中的轉(zhuǎn)換器列表



RestTemplate類圖

RestTemplate 類中省略了靜態(tài)成員變量、變量的set/get方法以及實(shí)現(xiàn)的接口方法,

RestOperations 接口中省略了參數(shù)類型及重載的方法。這樣既可以保證各個(gè)要素盡量在一幅圖中展現(xiàn),又不會(huì)影響理解。

postForEntity 處理過程

以postForEntity方法作為切入點(diǎn),來梳理一下請(qǐng)求是如何執(zhí)行的,以下概要流程圖。(灰色方框內(nèi)為doExcute方法的內(nèi)部處理。)

postForEntity方法中,創(chuàng)建了兩個(gè)內(nèi)部類對(duì)象requestCallback和responseExtractor并傳遞給execute方法,分別用于請(qǐng)求和響應(yīng)的關(guān)鍵處理。

總結(jié)了一下,不管是請(qǐng)求還是響應(yīng),這里的關(guān)鍵處理就是明確資源的媒體類型(也就是要明確請(qǐng)求端和響應(yīng)端交換的信息的格式),

根據(jù)媒體類型選擇適合的解析器,將消息寫入輸出流或者從輸入流讀入。



requestCallback.doWithRequest 處理過程

——內(nèi)部類AcceptHeaderRequestCallback.doWithRequest的處理。

發(fā)送請(qǐng)求時(shí),Http頭部需要設(shè)置Accept字段,該字段表明了發(fā)送請(qǐng)求的這方接受的媒體類型(消息格式),也是響應(yīng)端要返回的信息的媒體類型(消息格式)。

根據(jù)postForEntity方法的第三個(gè)參數(shù)responseType,程序?qū)⑦x擇適合的解析器XXXConverter,并依據(jù)該解析器找出所有支持的媒體類型。

——內(nèi)部類HttpEntityRequestCallback.doWithRequest的處理。

如果是POST請(qǐng)求并且消息體存在時(shí),除了設(shè)置Accept字段,還可能需要設(shè)置Content-Type字段,該字段表明了所發(fā)送請(qǐng)求的媒體類型(消息格式),也是響應(yīng)端接受的媒體類型(消息格式)。

根據(jù)postForEntity方法的第二個(gè)參數(shù)request,程序?qū)⑦x擇適合的解析器XXXConverter,將請(qǐng)求消息寫入輸出流。


responseExtractor.extractData 處理過程

與請(qǐng)求消息體的處理過程相似。

雖然,postForEntity方法中responseExtractor對(duì)象的類型為ResponseEntityResponseExtractor,但是實(shí)際執(zhí)行處理過程是HttpMessageConverterExtractor的對(duì)象實(shí)例。

在postForObject方法中,則是直接使用了HttpMessageConverterExtractor創(chuàng)建對(duì)象。

下圖畫出的也是HttpMessageConverterExtractor類中的extractData方法的處理過程。


關(guān)于GenericHttpMessageConverter

在以上幾個(gè)方法的梳理過程中,我注意到每次消息解析轉(zhuǎn)換都要作GenericHttpMessageConverter分支判斷,為什么呢?

GenericHttpMessageConverter接口繼承自HttpMessageConverter接口,二者都是在org.springframework.http.converter路徑下。

此包中還有其他幾種Converter實(shí)現(xiàn)類,看名字就可以猜到主要功能。唯獨(dú)GenericHttpMessageConverter沒猜出來。

于是,我在eclipse中使用Ctrl+Shift+G快捷鍵搜索了一下它的實(shí)現(xiàn)類AbstractGenericHttpMessageConverter。

看到AbstractJackson2HttpMessageConverter類的時(shí)候,我好像明白了。

GenericHttpMessageConverter是其他轉(zhuǎn)換器派生類的接口,用于解析特殊格式的資源,比如json,xml等。


關(guān)于RestTemplate 中的轉(zhuǎn)換器列表

轉(zhuǎn)換器列表messageConverters是final類型的,由RestTemplate的構(gòu)造函數(shù)賦值。一旦創(chuàng)建了RestTemplate對(duì)象,該對(duì)象也就同時(shí)擁有了一個(gè)當(dāng)前系統(tǒng)支持的轉(zhuǎn)換器列表。

那么,對(duì)于需要引用jar包的轉(zhuǎn)換器,RestTemplate是怎么添加轉(zhuǎn)換器實(shí)例的呢?

在聲明messageConverters列表之前,定義了幾個(gè)布爾型靜態(tài)常量,該常量是對(duì)某一個(gè)特殊類是否可以被加載的判斷結(jié)果。

在RestTemplate的構(gòu)造函數(shù)中,根據(jù)該常量值來判斷是否將某個(gè)轉(zhuǎn)換器的實(shí)例加入到列表中。

private static boolean romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", RestTemplate.class.getClassLoader());?

private static final boolean jaxb2Present =? ? ? ? ? ? ClassUtils.isPresent("javax.xml.bind.Binder", RestTemplate.class.getClassLoader());? ?

private static final boolean jackson2Present =? ? ? ? ? ? ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) &&? ? ? ? ? ? ? ? ? ? ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader());? ?

private static final boolean jackson2XmlPresent =? ? ? ? ? ? ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", RestTemplate.class.getClassLoader());? ?

private static final boolean gsonPresent =? ? ? ? ? ? ClassUtils.isPresent("com.google.gson.Gson", RestTemplate.class.getClassLoader());? ?

private final List> messageConverters = new ArrayList<>();


由此可知,RestTemplate的初始化順序:

創(chuàng)建(new)一個(gè)RestTemplate實(shí)例時(shí),首先裝載RestTemplate類,然后按照出現(xiàn)的順序轉(zhuǎn)載靜態(tài)變量或代碼。

裝載完成之后,進(jìn)行實(shí)例化。首先實(shí)例化成員變量,然后執(zhí)行構(gòu)造函數(shù)。

那么,外部引用的類是否可以被加載具體是怎么判斷的?

通過ClassUtils.isPresent(String className, ClassLoader classLoader)方法。——感覺ClassUtils可以單獨(dú)寫一篇orz

ClassUtils 類在 spring-core 工程的?org.springframework.util 路徑下。

簡(jiǎn)單來說,isPresent實(shí)際上是返回了ClassUtils.forName方法的處理結(jié)果,當(dāng)forName方法正常執(zhí)行,則鑒定的類被加載,返回true;若拋出異常(注意,此處異常是Throwable)則返回false。

forName方法的處理是:

首先,根據(jù)類名的長(zhǎng)度(<=8)來確定是否是原始類型,若是原始類型則返回類對(duì)象Class。

其次,判斷是否是普通類型,若是原始類型則返回類對(duì)象Class。

第三,判斷是否數(shù)組類型,通過過濾"["字符截取類名,遞歸調(diào)用forName方法獲取類對(duì)象,然后利用反射Array.newInstance創(chuàng)建對(duì)象并返回。

最后,排除以上情況后,使用classLoader來加載className指定的類。


補(bǔ)充

原始類型:Java的基本類型及其包裝類,基本類型的數(shù)組類對(duì)象,以及空類型void.class。

普通類型:

感謝閱讀,所有文字僅供學(xué)習(xí)參考,如有錯(cuò)誤或不足之處,歡迎留言

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 12,394評(píng)論 6 13
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,273評(píng)論 6 342
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,053評(píng)論 0 9
  • Grandmother Erbomu. Erbofu. Sanbomu. Sanbofu. Wubomu. Wub...
    佐娃閱讀 349評(píng)論 0 0

友情鏈接更多精彩內(nèi)容