restTemplate源码解析(五)处理ClientHttpResponse响应对象
所有文章
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风格的实现方式。