RestTemplate的远程调用实现原理

662 阅读4分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

最近工作中的项目刚好开始接触到RestTemplate的使用,作为基本没有经验又对业务无所了解的小白,同事的业务代码都给我带来了很大压力,待到明白业务后发现,RestTemplate才是业务的核心,遂又去学习了Spring的RestTemplate,今天就记录一下自己对RestTemplate远程调用的一些理解。

上一篇文章我们学习了在Springboot中创建RESTful风格的服务接口,并使用RestTemplate进行RESTful接口的访问,算是认识了RestTemplate的常用方法,详情请戳:SpringBoot实现RESTful服务接口

RestTemplate的远程调用实现原理

RestTemplate就是SpringBoot提供的用于访问RESTful服务的客户端模板工具类。

RestTemplate类

上次我们说到,在使用了RestTemplate时可以使用最简单有效的方式去创建一个工具类对象,例如:new RestTemplate();,那么我们这次就深入RestTemplate类的定义中去观察它是如何实现的。

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
    ...
    public RestTemplate() {
        this.messageConverters = new ArrayList();
        this.errorHandler = new DefaultResponseErrorHandler();
        this.headersExtractor = new RestTemplate.HeadersExtractor();
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        this.messageConverters.add(new ResourceHttpMessageConverter(false));
        if (!shouldIgnoreXml) {
            try {
                this.messageConverters.add(new SourceHttpMessageConverter());
            } catch (Error var2) {
                ;
            }
        }
    }
    ...
}

从RestTemplate类的源码定义中可以看出:

  1. RestTemplate继承了InterceptingHttpAccessor
  2. RestTemplate类实现了RestOperations接口
  3. RestTemplate类中的无参初始化方法中创建了很多转换器

RestOperations接口

我们首先来看一下RestOperations接口的定义:

public interface RestOperations {
    <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
    <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
    <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,Object... uriVariables) throws RestClientException;
    void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException;
    void delete(String url, Object... uriVariables) throws RestClientException;
    <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
    Class<T> responseType, Object... uriVariables) throws RestClientException;
	…
}

可以看出RestTemplate中提供的get/post/put/delete等方法都是在RestOperations接口中定义的,且这些方法都遵循RESTful风格,最后在RestTemplate类中进行了相关的逻辑实现。

InterceptingHttpAccessor抽象类

从名字可以看到该类是一个拦截器相关类,其源码定义为:

public abstract class InterceptingHttpAccessor extends HttpAccessor {
    private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
    private volatile ClientHttpRequestFactory interceptingRequestFactory;
    ...
}

我们看到在InterceptingHttpAccessor类的定义中包含两个变量定义,其中一个interceptors是分负责设置和管理请求拦截器 ClientHttpRequestInterceptor的集合,另一个则是负责获取用于创建客户端HTTP请求的工厂类 ClientHttpRequestFactory

另外,InterceptingHttpAccessor拦截器类作为一个抽象类,其本身又继承了HttpAccessor抽象类。

HttpAccessor抽象类

HttpAccessor作为顶层的抽象类,源码定义为:

public abstract class HttpAccessor {
    private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    …
}

其内部定义并实例化了ClientHttpRequestFactory对象,实例化是是通过其实现类SimpleClientHttpRequestFactory创建的。

继承实现关系图

从源码定义中的逻辑中,我们不难看出之间的层级继承关系。 image.png

由图分析可以得到,RestTemplate 的类层左边继承了HttpAccessor抽象类和InterceptingHttpAccessor抽象类,以此完成了与HTTP请求相关的实现机制;而右边实现了基于RESTful风格的RestOperations接口,并实现了接口中定义的方法。RestTemplate使用了面向对象中的机制,实现接口和继承抽象类来完成两个部分功能的聚合。

RestTemlpate内部执行流程

学习了RestTemlpate类的定义,接下来我们再看一下RestTemlpate类中定义的方法是怎么实现的。 我们上次说过,RestTemlpate中有get、post、put、delete、exchange、execute等方法,而通过方法的定义可以看出所有的方法最终都是通过execute方法去完成的。

@Nullable
    public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
        ...
        return this.execute(url, HttpMethod.GET, requestCallback, responseExtractor, (Object[])uriVariables);
    }
    
    public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException {
        return (HttpHeaders)nonNull(this.execute(url, HttpMethod.HEAD, (RequestCallback)null, this.headersExtractor(), (Object[])uriVariables));
    }
    
    @Nullable
    public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
        RequestCallback requestCallback = this.httpEntityCallback(request);
        HttpHeaders headers = (HttpHeaders)this.execute(url, HttpMethod.POST, requestCallback, this.headersExtractor(), uriVariables);
        return headers != null ? headers.getLocation() : null;
    }

因此,我们的核心就是execute方法,接下来我们主要来探究execute方法的执行流程。

execute方法定义

@Override
@Nullable
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
 
        URI expanded = getUriTemplateHandler().expand(url, uriVariables);
        return doExecute(expanded, method, requestCallback, responseExtractor);
}

可以看到,在execute方法中首先通过 UriTemplateHandler 构建了一个 URI,然后将请求过程委托给 doExecute 方法进行处理,看来这个调用还挺深的。那么我们再进入doExecute方法中查看

@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    Assert.notNull(url, "URI is required");
    Assert.notNull(method, "HttpMethod is required");
    ClientHttpResponse response = null;
    Object var14;
    try {
        //创建请求对象
        ClientHttpRequest request = this.createRequest(url, method);
        if (requestCallback != null) {
            requestCallback.doWithRequest(request);
        }
        
        //远程调用
        response = request.execute();
        
        //抽取返回结果
        this.handleResponse(url, method, response);
        var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
    } catch (IOException var12) {
        String resource = url.toString();
        String query = url.getRawQuery();
        resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
        throw new ResourceAccessException("I/O error on " + method.name() + " request for "" + resource + "": " + var12.getMessage(), var12);
    } finally {
        if (response != null) {
            response.close();
        }
    }
    return var14;
}

从方法流程中可以看出,整个远程调用流程中主要分为三步:

  1. 创建请求对象request
  2. 执行远程调用request.execute()
  3. 返回请求结果

创建请求对象

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
        ClientHttpRequest request = getRequestFactory().createRequest(url, method);
        if (logger.isDebugEnabled()) {
            logger.debug("Created " + method.name() + " request for \"" + url + "\"");
        }
        return request;
}

创建 ClientHttpRequest 的过程是一种典型的工厂模式应用场景,创建了一个实现 ClientHttpRequestFactory 接口的 SimpleClientHttpRequestFactory 对象,然后再通过这个对象的 createRequest 方法创建了客户端请求对象 ClientHttpRequest 并返回给上层组件进行使用。

执行请求

request 即前面创建的 SimpleBufferingClientHttpRequest 类,而execute方法定义为:

@Override
public final ClientHttpResponse execute() throws IOException {
        assertNotExecuted();
        ClientHttpResponse result = executeInternal(this.headers);
        this.executed = true;
        return result;
}

返回请求结果

HTTP请求处理的最后一步就是从ClientHttpResponse中读取输入流,然后格式化为一个响应体并将其转化为业务对象。我们看一下获取返回结果的handleResponse 方法:

protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
        ResponseErrorHandler errorHandler = getErrorHandler();
        boolean hasError = errorHandler.hasError(response);
        if (logger.isDebugEnabled()) {
            try {
                logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
                        response.getRawStatusCode() + " (" + response.getStatusText() + ")" +
                        (hasError ? "; invoking error handler" : ""));
            }
            catch (IOException ex) {
                // ignore
            }
        }
        if (hasError) {
            errorHandler.handleError(url, method, response);
        }
}

通过getErrorHandler方法获取了一个ResponseErrorHandler,如果响应的状态码错误,可以调用handleError来处理错误并抛出异常。 而获取响应数据并完成转化的工作是在 ResponseExtractor 中,在 RestTemplate 类中,我们定义了一个 ResponseEntityResponseExtractor 内部类实现了ResponseExtractor 接口,如下代码所示:

private class ResponseEntityResponseExtractor <T> implements ResponseExtractor<ResponseEntity<T>> {
        @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;
            }
        }
        
        @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();
            }
        }
}

代码中ResponseEntityResponseExtractor中的extractData方法本质上是将数据提取部分的工作委托给了一个代理对象delegate,这个delegate的类型就是HttpMessageConverterExtractor,HttpMessageConverterExtractor 类的内部使用了HttpMessageConverter实现消息的转换。

最后,通过RestTemplate发起、执行及响应整个HTTP请求的完整流程就介绍完毕了。