【Spring-web】RestTemplate源碼學習——梳理內部實現過程


2016-12-28 by 安靜的下雪天  http://www.cnblogs.com/quiet-snowy-day/p/6228198.html 

提示:使用手機瀏覽時請注意,圖多費流量。

 

本篇概要

 

RestTemplate類圖

RestTemplate 類中省略了靜態成員變量、變量的set/get方法以及實現的接口方法,

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

 

返回頂部

 

postForEntity 處理過程

以postForEntity方法作為切入點,來梳理一下請求是如何執行的,以下概要流程圖。(灰色方框內為doExcute方法的內部處理。)
postForEntity方法中,創建了兩個內部類對象requestCallback和responseExtractor並傳遞給execute方法,分別用於請求和響應的關鍵處理。
總結了一下,不管是請求還是響應,這里的關鍵處理就是明確資源的媒體類型(也就是要明確請求端和響應端交換的信息的格式),
根據媒體類型選擇適合的解析器,將消息寫入輸出流或者從輸入流讀入。

 

返回頂部

 

requestCallback.doWithRequest 處理過程

——內部類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<>();
View Code
由此可知,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。
 
普通類型:
 

 

返回頂部

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM