所有文章
https://www.cnblogs.com/lay2017/p/11740855.html
正文
上一篇文章中,我們執行了ClientHttpRequest與服務端進行交互。並返回了一個ClientHttpResponse的實例對象。
本文將繼續最后一個部分,處理請求后的響應。
同樣的,我們再次回顧一下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(); } } }
我們的關注重點在於execute之后做了哪些事情
handleResponse
先跟進handleResponse方法
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { ResponseErrorHandler errorHandler = getErrorHandler(); boolean hasError = errorHandler.hasError(response); if (logger.isDebugEnabled()) { try { int code = response.getRawStatusCode(); HttpStatus status = HttpStatus.resolve(code); logger.debug("Response " + (status != null ? status : code)); } catch (IOException ex) { // ignore } } if (hasError) { errorHandler.handleError(url, method, response); } }
getErrorHandler獲取了一個錯誤處理器,如果Response的狀態碼是錯誤的,那么就調用handleError處理錯誤並拋出異常。
extractData生成響應對象
跟進extractData方法
public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException { if (this.delegate != null) { T body = this.delegate.extractData(response); return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body); } else { return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build(); } }
這里主要是將狀態碼和相依你個對象包裝成一個ResponseEntity,然后返回。
我們看看body的生成。
先看一下delegate這個代理類是怎么來的
@Nullable private final HttpMessageConverterExtractor<T> delegate; public ResponseEntityResponseExtractor(@Nullable Type responseType) { if (responseType != null && Void.class != responseType) { this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); } else { this.delegate = null; } }
其實就是把RestTemplate構造方法中添加的HttpMessageConverter給包裝了一下
跟進delegate看看它的extractData方法吧
public T extractData(ClientHttpResponse response) throws IOException { MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response); if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) { return null; } MediaType contentType = getContentType(responseWrapper); try { for (HttpMessageConverter<?> messageConverter : this.messageConverters) { if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter<?>) messageConverter; // 判斷是否能夠讀取 if (genericMessageConverter.canRead(this.responseType, null, contentType)) { // 讀取 return (T) genericMessageConverter.read(this.responseType, null, responseWrapper); } } if (this.responseClass != null) { // 判斷是否能夠讀取 if (messageConverter.canRead(this.responseClass, contentType)) { // 讀取 return (T) messageConverter.read((Class) this.responseClass, responseWrapper); } } } } // ... }
核心邏輯就是遍歷HttpMessageConveter,如果能夠讀取數據,那么就調用read方法讀取數據。
那么,我們看看HttpMessageConverter的直接實現類AbstractHttpMessageConverter吧,閱讀一下它的read方法
public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return readInternal(clazz, inputMessage); }
再跟進readInternal方法,readInternal向下有很多實現,如
我們選擇一個簡單的實現看看,StringHttpMessageConverter類的readInternal方法
@Override protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException { Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType()); return StreamUtils.copyToString(inputMessage.getBody(), charset); }
String的轉換非常簡單,getBody方法將獲取到ClientHttpResponse中的輸入流。copyToString將從輸入流中讀取數據,並返回字符串結果。
打開copyToString看看
public static String copyToString(@Nullable InputStream in, Charset charset) throws IOException { if (in == null) { return ""; } StringBuilder out = new StringBuilder(); InputStreamReader reader = new InputStreamReader(in, charset); char[] buffer = new char[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = reader.read(buffer)) != -1) { out.append(buffer, 0, bytesRead); } return out.toString(); }
到這里,我們就使用HttpMessageConverter把響應實體對象生成了。正如前面說到的,會把狀態碼和響應實體對象包裝成ResponseEntity返回給用戶。用戶只需要通過get方法就可以獲取statusCode或者body了。
關閉輸入流
restTemplate還有最后一步操作需要去做,就是在finally塊中關閉輸入流
finally { if (response != null) { response.close(); } }
ClientHttpResponse的close方法將包含輸入流的關閉
@Override public void close() { try { if (this.responseStream == null) { getBody(); } StreamUtils.drain(this.responseStream); this.responseStream.close(); } catch (Exception ex) { // ignore } }
總結
到這里,restTemplate的源碼解讀文章就全部結束了。總的來說是一個基於Http請求響應模型的,采用了restful風格的實現方式。