本文系轉(zhuǎn)載文章,轉(zhuǎn)自博客園:安靜的下雪天 ?http://www.cnblogs.com/quiet-snowy-day/p/6228198.html
本篇概要
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ò)誤或不足之處,歡迎留言