最近這段時間用了下 RestTemplate 這個類,抽點時間總結下一些東西,但願對你們有所幫助。java
從 3.0 版本開始,Spring 提供了 RestTemplate 做爲用於訪問 Rest 服務的客戶端,RestTemplate 提供了多種便捷訪問遠程 Http 服務的方法,可以大大提升客戶端的編寫效率。api
本篇文章將從 RestTemplate 提供的 API 入手,先來了解下 RestTemplate 的具體使用,而後再對其中涉及到的幾個核心類進行分析,最後再來分析下 RestTemplate 執行的整個流程,篇幅比較長,建議先碼爲快!緩存
在平時的使用中,咱們一般都是使用包裝好的getForObject/getForEntity,postForObject/postForEntity/postForLocation,put以及delete。bash
getForEntity方法的返回值是一個ResponseEntity,ResponseEntity是Spring對HTTP請求響應的封裝,包括了幾個重要的元素,如響應碼、contentType、contentLength、響應消息體等。網絡
定義的一個controller資源:app
結果:異步
getForEntity(responseType=Map.class):{glmapper=hello glmapper}
getForEntity(responseType=String.class):{"glmapper":"hello glmapper"}
複製代碼
先來看下非map方式的,兩個controller,兩種不一樣方式的參數獲取(本質上是同樣的) 函數
getForObject 函數其實是對 getForEntity 函數的進一步封裝,若是隻關注返回的消息體的內容,對其餘信息都不關注,那麼就可使用 getForObject。post
這裏調用就比getForEntity要簡單一點了,能夠直接拿到對象:測試
getForObject 的幾個重載方法和 getForEntity 基本是同樣的。
在RestTemplate中,POST請求能夠經過以下三個方法來發起:postForEntity,postForObject,postForLocation。
postForEntity(URI url, @Nullable Object request, Class<T> responseType)
複製代碼
和 getForObject 相對應,只關注返回的消息體。
postForLocation也是提交新資源,提交成功以後,返回新資源的URI,postForLocation的參數和前面兩種的參數基本一致,只不過該方法的返回值爲Uri,這個只須要服務提供者返回一個Uri便可,該Uri表示新資源的位置。
這裏有點坑,咱們須要把這個uri添加到response的header中,否則後面拿到的是null。
exchange 方法和上述這些方法差異在於須要多一個請求類型的參數:
RestTemplate的異步實現方式。所涉及到的API和RestTemplate基本一致。區別在於RestTemplate直接返回結果,而AsyncRestTemplate返回的是ListenableFuture。
Spring提供了ClientHttpRequestInterceptor和AsyncClientHttpRequestInterceptor兩個接口,分別能夠對RestTemplate和AsyncRestTemplate發起的請求進行攔截,並在其被髮送至服務端以前修改請求或是加強相應的信息。
ClientHttpRequestInterceptor 攔截 RestTemplate
AsyncClientHttpRequestInterceptor 攔截AsyncRestTemplate
設置攔截器就是經過提供的 setInterceptors 設置便可:
ResponseErrorHandler 接口定義了當response發生錯誤時須要進行的操做。這裏咱們自定義一個CustomResponseErrorHandler,當返回的code不是200時,就表示執行出錯了。
設置 ResponseErrorHandler:
執行結果:
下面來梳理下 RestTemplate 中請求處理的流程。下圖中 XXXX 表示咱們調用的 API 方法。大致流程就是:api 內部作一些請求相關的處理封裝,而後交給 execute 方法執行,最後真正處理則是在 doExecute 方法中完成。
下面以 getForEntity 方法的執行過程來分析:
getForEntity 方法:
這個方法的做用就是建立一個 ClientHttpRequest 對象。RestTemplate集成了 HttpAccessor這個抽象類,建立ClientHttpRequest的過程就是在其父類HttpAccessor中經過默認的 ClientHttpRequestFactory 實現類 SimpleClientHttpRequestFactory 完成具體的請求建立。
一、建立 java.net.HttpURLConnection 對象
二、設置 connection,包括 connectTimeout、setDoInput 等。
三、bufferRequestBody 用於標誌是否使用緩存流的形式,默認是 true。缺點是當發送大量數據時,好比 put/post,存在內存消耗嚴重。該值能夠經過 SimpleClientHttpRequestFactory#setBufferRequestBody來修改。
不一樣版本的變動仍是比較大的,你們在閱讀源碼時,仍是從最新的代碼來看。
RequestCallback 封裝了請求體和請求頭對象。這裏會遍歷全部的 HttpMessageConverter,解析成全部支持的MediaType,放在allSupportedMediaTypes中。
request.getHeaders().setAccept(allSupportedMediaTypes);
複製代碼
RestTemplate中對應了兩個內部類的實現:
AcceptHeaderRequestCallback.doWithRequest的處理。 發送請求時,Http頭部須要設置Accept字段,該字段代表了發送請求的這方接受的媒體類型(消息格式),也是響應端要返回的信息的媒體類型(消息格式)。 根據postForEntity方法的第三個參數responseType,程序將選擇適合的解析器XXXConverter,並依據該解析器找出全部支持的媒體類型。
HttpEntityRequestCallback.doWithRequest的處理。 若是是POST請求而且消息體存在時,除了設置Accept字段,還可能須要設置Content-Type字段,該字段代表了所發送請求的媒體類型(消息格式),也是響應端接受的媒體類型(消息格式)。 根據postForEntity方法的第二個參數request,程序將選擇適合的解析器XXXConverter,將請求消息寫入輸出流。
這裏會把請求頭/體封裝到connect,而後發送請求。跟蹤 execute 方法執行,定位到SimpleBufferingClientHttpRequest#executeInternal方法:
最後就是 response 的解析了,從代碼來看,主要仍是 Error 的解析。這裏的ErrorHandler咱們前面也提到,能夠經過實現 ResponseErrorHandler 來自定義 異常處理。
本篇先介紹了RestTemplate的API使用,挑了幾個介紹了下,更多使用細節仍是要針對不一樣的場景來決定。接着對攔截器,異步RestTemplate以及錯誤處理器作了簡單的介紹並給出了案例。最後分析了下RestTemplate的執行流程,篇幅緣由執行流程部分只是大概捋了捋,其中仍是不少細節有時間再補充,這部分主要就是看底層是如何通訊的,已經請求參數的傳遞等。