上一篇文章,我们介绍了RestTemplate在各种场景下的使用,今天我们来分析一下它的源码。
类结构
RestOperations是一个接口,意为Rest操作,抽象出了具有restful风格的操作方法,我们常用的getForObject、postForEntity、exchange都是里面定义的接口方法。
HttpAccessor则是一个抽象类,意为Http访问器,内部保存了请求的工厂类,即ClientHttpRequestFactory类,使用createRequest生产ClientHttpRequest,RestTemplate最终会将请求构造成ClientHttpRequest,由ClientHttpRequest负责与服务端进行交互。
流程解析
入口:
result = restTemplate.getForObject("http://localhost:9001/producer", String.class);
getForObject是RestOperation中的方法
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
//获取请求回调
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
//获取http消息转化抽取器,HttpMessageConverterExtractor实现了ResponseExtractor,可以将http响应的文本数据转化成相应的java对象。
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
acceptHeaderRequestCallback(responseType)
public <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
// 返回了AcceptHeaderRequestCallback实例
return new AcceptHeaderRequestCallback(responseType);
}
进入execute方法中
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
//拼接uri
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
进入doExecute中【核心代码
】
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
// url和请求方法不能为null
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
//创建文章开头所说的ClientHttpRequest
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
//执行请求回调
requestCallback.doWithRequest(request);
}
//执行请求,获取响应结果
response = request.execute();
//处理响应结果
handleResponse(url, method, response);
//利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
createRequest(url, method),此处调用的是HttpAccessor中的createRequest方法
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
//this.getRequestFactory()返回默认的SimpleClientHttpRequestFactory
ClientHttpRequest request = this.getRequestFactory().createRequest(url, method);
this.initialize(request);
if (this.logger.isDebugEnabled()) {
this.logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}
其中getRequestFactory方法被InterceptingHttpAccessor(在类图结构中有该类)重写了
public ClientHttpRequestFactory getRequestFactory() {
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
可以看到,一上来先获取拦截器,不过我们并没有设置拦截器。因此直接返回了父类即HttpAccessor中的RequestFactory
public abstract class HttpAccessor {
/** Logger available to subclasses. */
protected final Log logger = HttpLogging.forLogName(getClass());
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
.....
}
而HttpAccessor中使用的是SimpleClientHttpRequestFactory类。
接着走进SimpleClientHttpRequestFactory的createRequest方法
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
//打开连接
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
//前期准备工作
prepareConnection(connection, httpMethod.name());
//默认为true
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
openConnection(uri.toURL(), this.proxy)
protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
if (!(urlConnection instanceof HttpURLConnection)) {
throw new IllegalStateException(
"HttpURLConnection required for [" + url + "] but got: " + urlConnection);
}
return (HttpURLConnection) urlConnection;
}
到这里,仿佛明白了什么,原来RestTemplate对原生的http请求URLConnection 进行了一层封装。
prepareConnection(connection, httpMethod.name())
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
//设置连接超时时间
if (this.connectTimeout >= 0) {
connection.setConnectTimeout(this.connectTimeout);
}
//设置读取超时时间
if (this.readTimeout >= 0) {
connection.setReadTimeout(this.readTimeout);
}
//设置请求方法等
boolean mayWrite =
("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod));
connection.setDoInput(true);
connection.setInstanceFollowRedirects("GET".equals(httpMethod));
connection.setDoOutput(mayWrite);
connection.setRequestMethod(httpMethod);
}
prepareConnection比较简单,进行了一些简单的设置工作。当我们自己利用URLConnection进行http请求时,这些设置也是我们必须要去做的,RestTemplate只是封装好了罢了
createRequest流程走完了,返回了一个封装URLConnection的ClientHttpRequest对象
接下来到了requestCallback.doWithRequest(request),即在执行请求之前,先去执行请求回调的doWithRequest方法
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (this.responseType != null) {
List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
.filter(converter -> canReadResponse(this.responseType, converter))
.flatMap((HttpMessageConverter<?> converter) -> getSupportedMediaTypes(this.responseType, converter))
.distinct()
.sorted(MediaType.SPECIFICITY_COMPARATOR)
.collect(Collectors.toList());
if (logger.isDebugEnabled()) {
logger.debug("Accept=" + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
接下来需要真正去执行调用了,即request.execute()
execute方法在AbstractClientHttpRequest类中
public final ClientHttpResponse execute() throws IOException {
//检查ClientHttpRequest是否被执行过,如果执行过,则直接报错
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
//在执行请求之后,将标志位设置为true
this.executed = true;
return result;
}
进入executeInternal方法中,位于AbstractBufferingClientHttpRequest中
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)方法,位于SimpleBufferingClientHttpRequest中
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
//将请求头设置进connection中
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()) {
//将缓存区的数据复制进connection的输出流中
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
// Immediately trigger the request in a no-output scenario as well
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
}
该方法使用HttpURLConnection与服务端建立了连接,并返回一个SimpleClientHttpResponse。单纯包裹着此HttpURLConnection对象。
request.execute()也就走完了,接着走主流程的下一步
handleResponse(url, method, response)
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
}
}
//如果出现错误的话,例如状态码为500,则进入到错误处理流程中
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
这一步主要是检查状态码是否是4或者5开头的,如果是的话,则走相应的错误处理流程。
如果没有错误的话,则进入到最后一步
responseExtractor.extractData(response),抽取响应中的数据
responseExtractor是HttpMessageConverterExtractor类型的
public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
MediaType contentType = getContentType(responseWrapper);
try {
//遍历HttpMessageConverter
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
if (logger.isDebugEnabled()) {
ResolvableType resolvableType = ResolvableType.forType(this.responseType);
logger.debug("Reading to [" + resolvableType + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
//如果该messageConverter能够读取该contentType,并且能转化成responseClass类型
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
//将响应数据转化成对应的java类型
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
catch (IOException | HttpMessageNotReadableException ex) {
throw new RestClientException("Error while extracting response for type [" +
this.responseType + "] and content type [" + contentType + "]", ex);
}
throw new UnknownContentTypeException(this.responseType, contentType,
response.getRawStatusCode(), response.getStatusText(), response.getHeaders(),
getResponseBody(response));
}
extractData和核心逻辑就是,遍历所有的HttpMessageConverter,如果发现某个messageConverter能够读取响应数据,则返回转化后的数据。
主流程最后一步,则是将封装的HttpURLConnection的输入流关闭。
到这里,RestTemplate的源码分析就结束了。
总结
一句话概括RestTemplate,RestTemplate封装了原生的HttpURLConnection,采用Restful的理念,更优雅地来完成对HTTP服务的调用。