所有文章
https://www.cnblogs.com/lay2017/p/11740855.html
正文
上一篇文章中,我們創建了一個ClientHttpRequest的實例。本文將繼續閱讀ClientHttpRequest的執行邏輯。
再次回顧一下restTemplate核心邏輯的代碼
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { ClientHttpResponse response = null; try { // 生成請求 ClientHttpRequest request = createRequest(url, method); if (requestCallback != null) { // 設置header requestCallback.doWithRequest(request); } // 執行請求,獲取響應 response = request.execute(); // 處理響應 handleResponse(url, method, response); // 獲取響應體對象 return (responseExtractor != null ? responseExtractor.extractData(response) : null); } catch (IOException ex) { // ... 拋出異常 } finally { if (response != null) { // 關閉響應流 response.close(); } } }
ClientHttpRequest的默認實現類是SimpleBufferingClientHttpRequest,我們先看看它的繼承關系
可以看到,ClientHttpRequest直接被AbstractClientHttpRequest繼承,所以我們先從AbstractClientHttpRequest實現的execute方法開始
跟進execute方法
@Override public final ClientHttpResponse execute() throws IOException { assertNotExecuted(); ClientHttpResponse result = executeInternal(this.headers); this.executed = true; return result; }
execute方法前置校驗executed這個flag,executeInternal執行完后打了個true的標記。所以一個ClientHttpRequest將只能被執行一次。
繼續跟進executeInternal方法,executeInternal方法由AbstractBufferingClientHttpRequest實現
@Override protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { byte[] bytes = this.bufferedOutput.toByteArray(); if (headers.getContentLength() < 0) { headers.setContentLength(bytes.length); } ClientHttpResponse result = executeInternal(headers, bytes); this.bufferedOutput = new ByteArrayOutputStream(0); return result; }
核心邏輯在executeInternal(headers, bytes)里,繼續跟進它
executeInternal(headers, bytes)由SimpleBufferingClientHttpRequest實現
@Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { addHeaders(this.connection, headers); // JDK <1.8 doesn't support getOutputStream with HTTP DELETE if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) { this.connection.setDoOutput(false); } if (this.connection.getDoOutput() && this.outputStreaming) { this.connection.setFixedLengthStreamingMode(bufferedOutput.length); } this.connection.connect(); if (this.connection.getDoOutput()) { // 寫到輸出流上 FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream()); } else { // Immediately trigger the request in a no-output scenario as well this.connection.getResponseCode(); } // 響應一個ClientHttpResponse對象 return new SimpleClientHttpResponse(this.connection); }
如果開啟了輸出流,那么FileCopyUtils.copy方法將會把緩沖數據寫入到輸出流里面。
注意:FileCopyUtils.copy方法在寫入完畢后會關閉輸出流,所以不需要外部顯式關閉。我們看copy方法
public static void copy(byte[] in, OutputStream out) throws IOException { Assert.notNull(in, "No input byte array specified"); Assert.notNull(out, "No OutputStream specified"); try { out.write(in); } finally { try { out.close(); } catch (IOException ex) { } } }
executerInternal(headers, bytes)方法最后會響應一個SimpleClientHttpResponse實例對象,單純地包裝了Connection對象。
SimpleClientHttpResponse(HttpURLConnection connection) { this.connection = connection; }
顯然,后續的處理就是從ClientHttpResponse中讀取輸入流,然后格式化成一個響應體,最后回收資源。下一篇內容講述這個
總結
執行ClientHttpRequest邏輯其實就是與服務端創建Connection連接(如果有需要寫入數據則寫入到輸出流),整體還是比較簡單的。