2016-12-28 by 安靜的下雪天 http://www.cnblogs.com/quiet-snowy-day/p/6228198.html
提示:使用手機瀏覽時請注意,圖多費流量。
本篇概要
- RestTemplate 類圖
- postForEntity 方法處理過程
- requestCallback.doWithRequest 方法處理過程
- responseExtractor.extractData 方法處理過程
- 關於GenericHttpMessageConverter
- 關於RestTemplate 中的轉換器列表
RestTemplate類圖
RestTemplate 類中省略了靜態成員變量、變量的set/get方法以及實現的接口方法,
RestOperations 接口中省略了參數類型及重載的方法。這樣既可以保證各個要素盡量在一幅圖中展現,又不會影響理解。

postForEntity方法中,創建了兩個內部類對象requestCallback和responseExtractor並傳遞給execute方法,分別用於請求和響應的關鍵處理。
總結了一下,不管是請求還是響應,這里的關鍵處理就是明確資源的媒體類型(也就是要明確請求端和響應端交換的信息的格式),
根據媒體類型選擇適合的解析器,將消息寫入輸出流或者從輸入流讀入。
——內部類AcceptHeaderRequestCallback.doWithRequest的處理。
發送請求時,Http頭部需要設置Accept字段,該字段表明了發送請求的這方接受的媒體類型(消息格式),也是響應端要返回的信息的媒體類型(消息格式)。
根據postForEntity方法的第三個參數responseType,程序將選擇適合的解析器XXXConverter,並依據該解析器找出所有支持的媒體類型。
——內部類HttpEntityRequestCallback.doWithRequest的處理。
如果是POST請求並且消息體存在時,除了設置Accept字段,還可能需要設置Content-Type字段,該字段表明了所發送請求的媒體類型(消息格式),也是響應端接受的媒體類型(消息格式)。
根據postForEntity方法的第二個參數request,程序將選擇適合的解析器XXXConverter,將請求消息寫入輸出流。


responseExtractor.extractData 處理過程
與請求消息體的處理過程相似。
雖然,postForEntity方法中responseExtractor對象的類型為ResponseEntityResponseExtractor,但是實際執行處理過程是HttpMessageConverterExtractor的對象實例。
在postForObject方法中,則是直接使用了HttpMessageConverterExtractor創建對象。
下圖畫出的也是HttpMessageConverterExtractor類中的extractData方法的處理過程。

返回頂部
關於GenericHttpMessageConverter
在以上幾個方法的梳理過程中,我注意到每次消息解析轉換都要作GenericHttpMessageConverter分支判斷,為什么呢?
GenericHttpMessageConverter接口繼承自HttpMessageConverter接口,二者都是在org.springframework.http.converter路徑下。
此包中還有其他幾種Converter實現類,看名字就可以猜到主要功能。唯獨GenericHttpMessageConverter沒猜出來。
於是,我在eclipse中使用Ctrl+Shift+G快捷鍵搜索了一下它的實現類AbstractGenericHttpMessageConverter。
看到AbstractJackson2HttpMessageConverter類的時候,我好像明白了。
GenericHttpMessageConverter是其他轉換器派生類的接口,用於解析特殊格式的資源,比如json,xml等。


關於RestTemplate 中的轉換器列表
轉換器列表messageConverters是final類型的,由RestTemplate的構造函數賦值。一旦創建了RestTemplate對象,該對象也就同時擁有了一個當前系統支持的轉換器列表。
那么,對於需要引用jar包的轉換器,RestTemplate是怎么添加轉換器實例的呢?
在聲明messageConverters列表之前,定義了幾個布爾型靜態常量,該常量是對某一個特殊類是否可以被加載的判斷結果。
在RestTemplate的構造函數中,根據該常量值來判斷是否將某個轉換器的實例加入到列表中。

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<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
由此可知,RestTemplate的初始化順序:
創建(new)一個RestTemplate實例時,首先裝載RestTemplate類,然后按照出現的順序轉載靜態變量或代碼。
裝載完成之后,進行實例化。首先實例化成員變量,然后執行構造函數。
那么,外部引用的類是否可以被加載具體是怎么判斷的?
通過ClassUtils.isPresent(String className, ClassLoader classLoader)方法。
——感覺ClassUtils可以單獨寫一篇orz
ClassUtils 類在 spring-core 工程的 org.springframework.util 路徑下。
簡單來說,isPresent實際上是返回了ClassUtils.forName方法的處理結果,當forName方法正常執行,則鑒定的類被加載,返回true;若拋出異常(注意,此處異常是Throwable)則返回false。
forName方法的處理是:
首先,根據類名的長度(<=8)來確定是否是原始類型,若是原始類型則返回類對象Class<?>。
其次,判斷是否是普通類型,若是原始類型則返回類對象Class<?>。
第三,判斷是否數組類型,通過過濾"["字符截取類名,遞歸調用forName方法獲取類對象,然后利用反射Array.newInstance創建對象並返回。
最后,排除以上情況后,使用classLoader來加載className指定的類。
補充
原始類型:Java的基本類型及其包裝類,基本類型的數組類對象,以及空類型void.class。

普通類型:
