RestTemplate 源码解析与ClientHttpRequestInterceptor拦截器的使用

使用过Spring的对RestTemplate的使用应该非常了解了,它是Spring提供的Http请求客户端工具,本篇博客不再介绍RestTemplate的使用,仅仅对RestTemplate的调用流程进行解析,如果想要了解RestTemplate的使用,可以自行查看Spring的官方文档的Spring MVC部分。首先我们看RestTemplate的类的层次图与提供的调用方法,如下RestTemplate提供了很多的API供我们调用,下面仅仅展示一部分:

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
    ......
   return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)throws RestClientException {
    ......
    return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
}
......

从上面提供的方法可以看出,他们最终调用了execute()方法,下面我们查看execute()方法的逻辑:

public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
    //调用UriTemplateHandler对URI进行处理
    URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    //调用doExecuute()方法
    return doExecute(expanded, method, requestCallback, responseExtractor);
}

在execute()方法中,首先通过UriTemplateHandler对URI进行处理,使用URI变量对URI进行扩展,如下为Spring提供的UriTemplateHandler,它默认实现为DefaultUriBuilderFactory,在RestTemplate中代码与实现如下:

private UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory();

接下来我们查看doExecute()方法,其大概可以分为三个部分,创建请求,执行请求和处理请求结果,下面我们先分析RestTemplate是如何创建请求的。

protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    ......
    ClientHttpResponse response = null;
    try {
        //获取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);
    }
    ......
}

RestTemplate使用createRequest方法创建,createRequest方法是HttpAccessor的方法,从上面的类层次结构图可以看出RestTemplate继承了HttpAccessor,它通过getRequestFactory()获取一个请求工厂创建一个请求客户端ClientHttpRequest实例。

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
    //通过工厂类创建HTTP请求客户端
    ClientHttpRequest request = getRequestFactory().createRequest(url, method);
    return request;
}

接着我们查看是如何获取请求工厂的,getRequestFactory()是在InterceptingHttpAccessor中实现的,它继承了HttpAccessor,其实RestTemplate真正集成的是InterceptingHttpAccessor。如下代码创建了一个ClientHttpRequestFactory实例,

public ClientHttpRequestFactory getRequestFactory() {
    //获取请求拦截器ClientHttpRequestInterceptor实例
    List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
    //如果拦截器不为空,则通过默认的工厂和拦截器新构造一个InterceptingClientHttpRequestFactory实例
    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实例,否则返回一个默认的实例,如下为Spring为我们提供的默认ClientHttpRequestFactory实例:

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

 除了SimpleClientHttpRequestFactory的实现之外,我们可以我们可以通过restTemplate.setRequestFactory(requestFactory)设置工厂实例,如下为ClientHttpRequestFactory的实现:

 如何创建ClientHttpRequest实例这里不在讲述,其核心就是创建一个HTTP的请求的客户端,添加一些请求所需要的参数等内容,我们重点关注的是如何执行HTTP请求过程,这里有一个非常重要的接口即前面所介绍的拦截器:ClientHttpRequestInterceptor,它会在请求执行之前拦截请求,执行拦截器,这里我们可以做很多事,比如添加请求头,更改请求的URL,授权等等,部分代码如下所示:

public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
    //如果不为空执行拦截器,否则执行请求
    if (this.iterator.hasNext()) {
        ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
        return nextInterceptor.intercept(request, body, this);
    }
    //执行拦截器完毕之后发送请求
    .....
}

因为ClientHttpRequestInterceptor是遍历执行的,所以为了保证请求执行,我们在自定义的ClientHttpRequestInterceptor必须调用上面的execute方法,在执行拦截器时,已经传入了InterceptingRequestExecution实例,因此我们直接调用即可。下面我们查看Spring提供的一个授权拦截器,代码如下所示:

public class BasicAuthenticationInterceptor implements ClientHttpRequestInterceptor{
    private final String username;
    private final String password;
    @Nullable    
    private final Charset charset;
    //传入用户名和密码
    public BasicAuthenticationInterceptor(String username, String password){
        this(username, password, null);
    }
    public BasicAuthenticationInterceptor(String username, String password, @Nullable Charset charset){
        Assert.doesNotContain(username, ":", "Username must not contain a colon");
        this.username = username;
        this.password = password;
        this.charset = charset;
    }
  
  public ClientHttpResponse intercept(HttpRequest request, byte[] body,ClientHttpRequestExecution execution)throws IOException {
    HttpHeaders headers = request.getHeaders();
    //如果请求头包含Authorization设置BasicAuth
    if (!headers.containsKey("Authorization")) {
        headers.setBasicAuth(this.username, this.password, this.charset);
    }
    return execution.execute(request, body);
  }
}

Ribbon的负载均衡也是使用实现ClientHttpRequestInterceptor来实现的,如果感兴趣可以参考栏目微服务技术栈的内容Spring Cloud Netflix 负载均衡组件—Ribbon 高级应用

猜你喜欢

转载自blog.csdn.net/wk19920726/article/details/108754574