Spring RestTemplate 詳解

一、基本概念

Spring RestTemplate 是 Spring 提供的用於訪問 Rest 服務的客戶端,RestTemplate 提供了多種便捷訪問遠程Http服務的方法,可以大大提升客戶端的編寫效率,因此不少客戶端好比 Android或者第三方服務商都是使用 RestTemplate 請求 restful 服務。html

 

二、RestTemplate 調用流程

調用 RestTemplate 的默認構造函數,RestTemplate 對象在底層經過使用 java.net 包下的實現建立 HTTP 請求,能夠經過使用 ClientHttpRequestFactory 指定不一樣的HTTP請求方式。默認使用 SimpleClientHttpRequestFactory,是 ClientHttpRequestFactory 實現類。以下流程:java

1)使用默認構造方法new一個實例spring

RestTemplate template = new RestTemplate();數據庫

2)RestTemplate 內部經過調用 doExecute 方法,首先就是獲取 ClientHttpRequest緩存

 

3)RestTemplate 實現了抽象類 HttpAccessor ,因此能夠調用父類的 createRequestrestful

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();異步

public ClientHttpRequestFactory getRequestFactory() {函數

return this.requestFactory;post

}測試

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {

ClientHttpRequest request = getRequestFactory().createRequest(url, method);

if (logger.isDebugEnabled()) {

logger.debug("Created " + method.name() + " request for \"" + url + "\"");

}

return request;

}

 

4)SimpleClientHttpRequestFactory 實現了 ClientHttpRequest,同時實現方法

注意 bufferRequestBody 是能夠在 RestTemplate 設置,是標誌是否使用緩存流的形式,默認是 true,缺點是當發送大量數據時,好比put/post的保存和修改,那麼可能內存消耗嚴重。因此這時候能夠設置 RestTemplate.setBufferRequestBody(false);

即便用 SimpleStreamingClientHttpRequest 來實現。

 

5)openConnection 沒什麼文章,而是 prepareConnection 則是大有文章,這裏咱們分兩個版原本說,由於咱們一開始使用 4.1.1 的時候不能使用帶請求體的delete,但是在 4.3.2 版本則可使用,因此特別區分了這兩個版本的代碼,以下:

SimpleClientHttpRequestFactory -- 4.1.1 版本的代碼默認

delete connection.setDoOutput = fase

若是設置false,而後後面又去獲取輸出流時,會發生以下錯誤 sun 包的 HttpURLConnection

if(!this.doOutput) {

throw new ProtocolException(

"cannot write to a URLConnection if doOutput=false - call setDoOutput(true)"

);

}

 

SimpleClientHttpRequestFactory -- 4.3.2 版本的代碼默認

delete connection.setDoOutput = fase

DoOutput 的屬性做用是可使用 conn.getOutputStream().write() ,這樣就能發送請求體了

 

6)接着執行 requestCallback.doWithRequest(request);

RequestCallback 封裝了請求體和請求頭對象,也就是說在該對象裏面能夠拿到咱們須要的請求參數,在執行 doWithRequest 時,有一個很是重要的步驟,他和前面Connection發送請求體有着密切關係,咱們知道請求頭就是 SimpleBufferingClientHttpRequest.addHeaders 方法,那麼請求體 bufferedOutput 是如何賦值的呢?就是在 doWithRequest 裏面,以下 StringHttpMessageConverter (其餘 MessageConvert 也同樣,這裏也是常常亂碼的緣由)

其中 s 就是請求體,HttpOutputMessage 對象就是咱們準備的 ClientHttpRequest 對象,也就是上面的 SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest

這樣,先調用父類的流方法,把內容寫入流中,而後調用父類的 executeInternal方法在調用自身的該方法 executeInternal ,以下一步

 

7)接着執行 response = request.execute();

而後使用實例 SimpleBufferingClientHttpRequest 封裝請求體和請求頭

SimpleBufferingClientHttpRequest -- 4.1.1 版本的代碼默認

delete 時經過前面設置的 DoOutput 參數和是否能夠設置輸出流來判斷是否須要發送請求體

若是是 delete 請求,那麼很明顯 DoOutput = false,因此不會有封裝請求體的過程,即不執行

FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

因此服務端沒法獲取到請求體,會出現 HttpMessageNotReadableException: Required request body is missing

 

SimpleBufferingClientHttpRequest -- 4.3.2 版本的代碼默認

delete 時經過請求方式和是否有請求體對象來判斷是否須要發送請求體

若是是delete請求,首先設置 DoOutput = true,而後根據是否有請求體數據,而後封裝請求體

FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

 

8)最後解析response

接着就是 response 的解析了,主要仍是 Error 的解析。

handleResponseError(method, url, response);

 

三、RestTemplate 的配置項

1)setBufferRequestBody 是不是否緩衝流來存儲請求體,默認true

2)setProxy 設置代理對象

3)setChunkSize 設置每次傳輸字節長度,與 setBufferRequestBody(false) 結合使用

4)setConnectTimeout 設置鏈接超時時間,默認 -1

5)setReadTimeout 設置讀取內容超時時間,默認 -1

6)setOutputStreaming 設置Connection是否設置輸出流程

7)setTaskExecutor 設置異步回調執行器

 

四、RestTemplate 設置 RequestFactory

其實任何有鏈接的地方都會有鏈接池的概念,好比數據庫鏈接等,這裏也不例外,確定也會有,RestTemplate 默認有兩種工廠對象實現方式,都是 ClientHttpRequestFactory 的子類。以下

1)SimpleClientHttpRequestFactory 底層使用 java.net.HttpUrlConnection,可配置證書

2)HttpComponentsClientHttpRequestFactory 底層使用Apache HttpClient訪問遠程的Http服務,使用HttpClient一樣能夠配置鏈接池和證書等信息,並且功能更強大,配置項更多。

 

五、RequestFactory 的配置方式

1)使用XML配置,就是配置JavaBean

2)使用代碼配置,就是初始化這個對象

不管上面那種方式配置,都是配置外殼 RestTemplate,真正發送請求的 request 對象其實都是由工廠管理的,因此咱們不關心鏈接池的管理,只是配置鏈接池初始化的一些參數而已。

這個能夠參考:

http://www.open-open.com/lib/view/open1436018677419.html

 

六、請求參數的傳遞

 

七、關於網上說的沒法發送delete請求體

HttpMessageNotReadableException: Required request body is missing

Spring MVC 的 @RequestBody 只支持RestTemplate 的 POST 和 PUT

可是 RestTemplate 的 delete 方法並不支持傳入請求體(Request Body)。經測試,經過調用 RestTemplate 類的exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<ResponseResult> responseType, Object... uriVariables)  方法,將 method 指定爲 org.springframework.http.HttpMethod.DELETE,並傳入 requestEntity(請求體) 對象時,在服務端獲得的 Request Body 仍然爲 null。可見 RestTemplate 默認並不支持對 DELETE 方法使用請求體。

    經過查閱資料發現 RestTemplate 默認是使用 spring 自身的 SimpleClientHttpRequestFactory 建立請求對象和對其進行相關設置(如請求頭、請求體等),它只支持 PUT 和 POST 方法帶請求體,RestTemplate 的 DELETE 方法不支持傳入請求體是由於 JDK 中 HttpURLConnection 對象的 delete 方法不支持傳入請求體(若是對 HttpURLConnection 對象的 delete 方法傳入請求體,在運行時會拋出 IOException)。

從代碼中也看到了 Spring 對 delete 作了判斷,若是是 4.1.1 及之前的版本,確實是會出現上面的問題,可是當我使用 4.3.2 以後的版本,發現徹底能夠發送請求體,這裏面的變化就是前者在代碼中把請求體過濾掉了,後者把請求體加上了。至於更細的細節,但願有人可以繼續深究下去。

相關文章
相關標籤/搜索