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风格的实现方式。
最新文章
- WCF学习之旅—实现支持REST服务端应用(二十三)
- 【09-26】hibernate学习笔记
- MySQL日志
- linux下截取整个网页
- 使用ajax获取JSON数据的jQuery代码的格式
- java 小知识点
- JS模块式开发
- Nancy 简单学习
- Codeforces Round #291 (Div. 2) C - Watto and Mechanism 字符串
- C#开发客户端、JAVA和tomcat开发服务端
- information_schema.routines 学习
- 针对苹果最新审核要求 为应用兼容IPv6
- Java-if 嵌套结构
- request.setAttribute和request.getAttribute还有session.setAttribute和session.getAttribute还有request.getParameter和request.getAttribute区别和联系
- [物理学与PDEs]第3章习题5 一维理想磁流体力学方程组的数学结构
- fillder--修改返回数据
- MySQL8.0 关闭二进制日志
- Eclipse在Debug模式下经常进入ThreadPoolExecutor解决办法
- 协议无关组播-密集模式 PIM-DM
- android开发:Android 中自定义属性(attr.xml,TypedArray)的使用