ResponseExtractor
响应提取器:从Response
中提取数据。RestTemplate
请求完成后,都是通过它来从ClientHttpResponse
提取出指定内容(比如请求头、请求Body体等)~
它的直接实现似乎只有HttpMessageConverterExtractor,当然它也是最为重要的一个实现,和HttpMessageConverter相关。
在解释它之前,先看看这个:MessageBodyClientHttpResponseWrapper,它的特点:它不仅可以通过实际读取输入流来检查响应是否有消息体,还可以检查其长度是否为0(即空)
// @since 4.1.5 它是一个访问权限是default的类,是对其它ClientHttpResponse的一个包装 class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse { private final ClientHttpResponse response; // java.io.PushbackInputStream @Nullable private PushbackInputStream pushbackInputStream; // 判断相应里是否有body体 // 若响应码是1xx 或者是204;或者getHeaders().getContentLength() == 0 那就返回false 否则返回true public boolean hasMessageBody() throws IOException { HttpStatus status = HttpStatus.resolve(getRawStatusCode()); if (status != null && (status.is1xxInformational() || status == HttpStatus.NO_CONTENT || status == HttpStatus.NOT_MODIFIED)) { return false; } if (getHeaders().getContentLength() == 0) { return false; } return true; } // 上面是完全格局状态码(ContentLength)来判断是否有body体的~~~这里会根据流来判断 // 如果response.getBody() == null,返回true // 若流里有内容,最终就用new PushbackInputStream(body)包装起来~~~ public boolean hasEmptyMessageBody() throws IOException { ... } ... // 其余接口方法都委托~ @Override public InputStream getBody() throws IOException { return (this.pushbackInputStream != null ? this.pushbackInputStream : this.response.getBody()); } }
它的作用就是包装后,提供两个方法hasMessageBody、hasEmptyMessageBody
方便了对body体内容进行判断
// @since 3.0 泛型T:the data type public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> { // java.lang.reflect.Type private final Type responseType; // 这个泛型也是T,表示数据的Class嘛~ // 该calss有可能就是上面的responseType @Nullable private final Class<T> responseClass; // 重要:用于消息解析的转换器 private final List<HttpMessageConverter<?>> messageConverters; ... // 省略构造函数 // 从ClientHttpResponse 里提取值 @Override @SuppressWarnings({"unchecked", "rawtypes", "resource"}) public T extractData(ClientHttpResponse response) throws IOException { MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response); // 若没有消息体(状态码不对 或者 消息体为空都被认为是木有) if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) { return null; } // content-type若响应头header里没有指定,那默认是它MediaType.APPLICATION_OCTET_STREAM MediaType contentType = getContentType(responseWrapper); // 遍历所有的messageConverters,根据contentType 来选则一个消息转换器 // 最终return messageConverter.read((Class) this.responseClass, responseWrapper) ... } }
它的处理逻辑理解起来非常简单:利用contentType
找到一个消息转换器,最终HttpMessageConverter.read()
把消息读出来转换成Java对象。
它还有两个内部类的实现如下(都是RestTemplate
的私有内部类):
RestTemplate: // 提取为`ResponseEntity` 最终委托给HttpMessageConverterExtractor完成的 private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> { @Nullable private final HttpMessageConverterExtractor<T> delegate; public ResponseEntityResponseExtractor(@Nullable Type responseType) { // 显然:只有请求的返回值不为null 才有意义~ if (responseType != null && Void.class != responseType) { this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); } else { this.delegate = null; } } // 数据提取。都是交给`delegate.extractData(response)`做了,然后new一个ResponseEntity出来包装进去 // 若木有返回值(delegate=null),那就是一个`ResponseEntity`实例,body为null @Override 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(); } } } // 提取请求头 private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> { @Override public HttpHeaders extractData(ClientHttpResponse response) { return response.getHeaders(); } }
UriTemplateHandler
这个组件它用于定义用变量扩展uri模板的方法。
// @since 4.2 出现较晚 // @see RestTemplate#setUriTemplateHandler(UriTemplateHandler) public interface UriTemplateHandler { URI expand(String uriTemplate, Map<String, ?> uriVariables); URI expand(String uriTemplate, Object... uriVariables); }
关于URI的处理,最终都是委托给UriComponentsBuilder来完成。若对这块还存在一定疑问的,强烈强烈强烈 参考这里
推荐阅读
RestTemplate的使用和原理你都烂熟于胸了吗?【享学Spring MVC】
总结
本文介绍的组件是去理解RestTemplate必备的组件们,属于开山篇。因为RestTemplate使用频繁,并且经常需要调优,因此我寄希望大家也能对它做较为深入的了解,这也是我写本系列的目的,共勉。