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

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

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

 

本篇概要

 

RestTemplate類圖

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

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分支判斷,爲何呢?json

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。
 
普通類型:
 

 

返回頂部
相關文章
相關標籤/搜索