SpringCloud解析一:RestTemplate源码分析

在我们使用Spring框架进行应用开发的时候,RestTemplate组件的使用越来越频繁,在这里我简单的梳理下它的源码,从而更深入的理解其工作原理,同时也为后面的Ribbon的使用打下基础。

首先我们从一张较为简洁的类图开始,从全局了解其继承关系

RestTemplate继承关系

1,HttpAccessor抽象类

在该抽象类中重点关注setRequestFactory、getRequestFactory与createRequest三个方法。顾名思义,setRequestFactory与getRequestFactory分别就是为了设置属性requestFactory的值以及返回该值,createRequest方法是根据请求的URI地址以及请求的HTTP方法得到一个ClientHttpRequest类型的对象。

默认其requestFactory属性为ClientHttpRequestFactory类型的SimpleClientHttpRequestFactory实例对象。该实例的内部将使用JDK自带的HttpURLConnection来建立http连接,如下所示

@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
  HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
  prepareConnection(connection, httpMethod.name());
  if (this.bufferRequestBody) {
    return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
  } else {
    return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
  }
}

重点:RequestFactory必须是ClientHttpRequestFactory类型的。 除了这里默认的SimpleClientHttpRequestFactory,常用的还有InterceptingClientHttpRequestFactory。

2,InterceptingHttpAccessor抽象类

该类重写了HttpAccessor中的getRequestFactory方法,如果其中的拦截器(interceptors)为空,则直接调用父类的getRequestFactory方法直接返回,否则实例化InterceptingClientHttpRequestFactory工厂对象并返回,如下代码所示

@Override
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();
  }
}

其InterceptingClientHttpRequestFactory的createRequest方法将返回一个ClientHttpRequest类型的InterceptingClientHttpRequest实例对象

而默认的SimpleClientHttpRequestFactory返回的是ClientHttpRequest类型的SimpleBufferingClientHttpRequest或者SimpleStreamingClientHttpRequest

@Override
public final ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
  return createRequest(uri, httpMethod, this.requestFactory);
}

@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
  return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}

重点:使用不同的RequestFactory返回具有不同功能的ClientHttpRequest 类型的实例对象,然后通过调用其 execute() 方法来得到ClientHttpResponse响应对象。

在返回ClientHttpResponse类型对象的过程中具体要做些什么事情完全由ClientHttpRequest的实现类自我决定。

3,RestOperations接口

该接口主要是定义了一些访问方法,例如getForObject、getForEntity等,简要列举如下:

public interface RestOperations {
  <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
  HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException;
  <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
  Map<String, ?> uriVariables) throws RestClientException;
  <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException;
  <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException;
}

4,RestTemplate类

在RestTemplate的构造方法中会初始化许多消息转换器(HttpMessageConverter),以及用于解析URL模板(字符串)的UriTemplateHandler类型对象(DefaultUriBuilderFactory),其它的都是对父级接口的实现。


接下来我们根据一个方法调用来跟踪其执行流程

从上可知,RestTemplate的实现原理其实还挺简单的,很多时候其默认的配置就已经够我们使用了,那么哪些地方可能是我们可以进行扩展时需要考虑的地方呢,或者我们要对其功能进行扩展时应该从哪里入手呢?

RestTemplate中进行扩展最重要的地方就是ClientHttpRequestFactory与ClientHttpRequestInterceptor了。通过自定义ClientHttpRequestFactory我们可以使用apache Http Client或者okHttp组件来发起http的请求操作,同时返回我们自定义的ClientHttpRequest及ClientHttpResponse对象; 而通过自定义的ClientHttpRequestInterceptor对象,我们可以在发起请求的时候对请求进行拦截,对请求的参数等进行修改等。

下面我们从一个请求发起开始来分析整个流程,假定我们调用RestTemplate的getForObject方法

//调用RestTemplate的该方法发起请求
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
  RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
  HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
  return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}

public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
  //对URL模板字符串进行解析
  URI expanded = getUriTemplateHandler().expand(url, uriVariables);
  //执行请求【参照下面的方法】
  return doExecute(expanded, method, requestCallback, responseExtractor);
}

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;
  try {
    //针对要访问的URL和使用的HTTP方法创建请求对象【参照下面的方法】
    ClientHttpRequest request = createRequest(url, method);
    if (requestCallback != null) {
      requestCallback.doWithRequest(request);
    }
    //执行请求得到响应结果
    response = request.execute();
    //对响应结果进行处理
    handleResponse(url, method, response);
    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();
    }
  }
}

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
  //获取请求对象工厂并创建请求对象【参照下面的方法】
  ClientHttpRequest request = getRequestFactory().createRequest(url, method);
  initialize(request);
  if (logger.isDebugEnabled()) {
    logger.debug("HTTP " + method.name() + " " + url);
  }
  return request;
}
//重写父类方法,根据是否设置拦截器来返回合适的工厂对象
public ClientHttpRequestFactory getRequestFactory() {
  List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
  if (!CollectionUtils.isEmpty(interceptors)) {
    //如果有设置拦截器,则使用InterceptingClientHttpRequestFactory工厂对象
    ClientHttpRequestFactory factory = this.interceptingRequestFactory;
    if (factory == null) {
      factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
      this.interceptingRequestFactory = factory;
    }
    return factory;
  } else {
    return super.getRequestFactory();
  }
}

InterceptingClientHttpRequestFactory类的实现如下

public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
  //所有的拦截器对象
  private final List<ClientHttpRequestInterceptor> interceptors;
  
  public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,@Nullable List<ClientHttpRequestInterceptor> interceptors) {
    super(requestFactory);
    this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());
  }

   //重写父类的方法返回针对具有拦截器情况的InterceptingClientHttpRequest对象
  @Override
  protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
    return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
  }
}

InterceptingClientHttpRequest类的实现如下

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {
  private final ClientHttpRequestFactory requestFactory;
  private final List<ClientHttpRequestInterceptor> interceptors;
  private HttpMethod method;
  private URI uri;

  protected InterceptingClientHttpRequest(ClientHttpRequestFactory requestFactory,List<ClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod method) {
    this.requestFactory = requestFactory;
    this.interceptors = interceptors;
    this.method = method;
    this.uri = uri;
  }

  @Override
  public HttpMethod getMethod() {
    return this.method;
  }

  @Override
  public String getMethodValue() {
    return this.method.name();
  }

  @Override
  public URI getURI() {
    return this.uri;
  }

  @Override
  protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
    InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
    return requestExecution.execute(this, bufferedOutput);
  }
  //创建一个ClientHttpRequestExecution类型的内部类来执行所有拦截器并发起请求
  private class InterceptingRequestExecution implements ClientHttpRequestExecution {
    private final Iterator<ClientHttpRequestInterceptor> iterator;
    public InterceptingRequestExecution() {
      this.iterator = interceptors.iterator();
    }

    //该方法在拦截器中也会被调用,从而实现迭代执行所有拦截器的效果
    @Override
    public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
      if (this.iterator.hasNext()) {
        ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
        return nextInterceptor.intercept(request, body, this);
      } else {
        //当所有拦截器都执行完成后便开始发起HTTP请求
        HttpMethod method = request.getMethod();
        Assert.state(method != null, "No standard HTTP method");
        //通过设置的requestFactory来创建ClientHttpRequest类型的对象并发起请求
        //可以通过改变requestFactory来改变默认的行为
        ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
        request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));

        if (body.length > 0) {
          if (delegate instanceof StreamingHttpOutputMessage) {
            StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
            streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
          } else {
            StreamUtils.copy(body, delegate.getBody());
          }
        }
        //执行请求操作
        return delegate.execute();
      }
    }
  }
}

在调用execute()方法时会间接的调用executeInternal方法并返回。在该方法里面创建了一个InterceptingRequestExecution对象,InterceptingRequestExecution对象的execute方法在执行的过程中会被迭代的调用(被各个拦截器调用),直到所有的拦截器都执行完后才开始创建要执行HTTP请求的ClientHttpRequest对象,然后执行并返回结果。

我们找一个BasicAuthorizationInterceptor拦截器看其内部实现

public class BasicAuthorizationInterceptor implements ClientHttpRequestInterceptor {
  private final String username;
  private final String password;

  public BasicAuthorizationInterceptor(@Nullable String username, @Nullable String password) {
    Assert.doesNotContain(username, ":", "Username must not contain a colon");
    this.username = (username != null ? username : "");
    this.password = (password != null ? password : "");
  }

  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    //加上需要的请求头数据
    String token = Base64Utils.encodeToString((this.username + ":" + this.password).getBytes(StandardCharsets.UTF_8));
    request.getHeaders().add("Authorization", "Basic " + token);
    //继续执行后面的拦截器对象
    return execution.execute(request, body);
  }
}

重点:可见,RestTemplate在执行调用的时候主要就是经过了以下三步

  1. 使用ClientHttpRequestFactory类型对象创建ClientHttpRequest类型对象【例如可以执行拦截器的 InterceptingClientHttpRequest 】
  2. 执行ClientHttpRequest类型实例对象的execute()方法得到ClientHttpResponse类型的响应结果
  3. 对响应结果进行处理并返回给调用者

小结

通过以上的分析,应该已经可以很好的理解RestTemplate的执行原理了,我们也可以通过自定义RequestFactory和拦截器的方式来扩展其功能,例如使用RestTemplate完成客户端负载均衡等。最后提供一张较为完整的类图,供大家参考


RestTemplate类图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容