RestTemplate source code analysis and the use of ClientHttpRequestInterceptor interceptor

Those who have used Spring should have a good understanding of the use of RestTemplate. It is a Http request client tool provided by Spring. This blog will not introduce the use of RestTemplate, but only analyze the call flow of RestTemplate. If you want to know the use of RestTemplate , You can check the Spring MVC section of Spring's official documentation. First, let's look at the hierarchy diagram of the RestTemplate class and the calling methods provided. The following RestTemplate provides a lot of APIs for us to call, and only a part of it is shown below:

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));
}
......

As can be seen from the methods provided above, they finally called the execute() method. Below we look at the logic of the execute() method:

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);
}

In the execute() method, the URI is first processed through UriTemplateHandler, and the URI is extended using URI variables. The following is the UriTemplateHandler provided by Spring, which is implemented as DefaultUriBuilderFactory by default. The code and implementation in RestTemplate are as follows:

private UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory();

 

Next we look at the doExecute() method, which can be roughly divided into three parts, creating a request, executing the request and processing the result of the request. Let's first analyze how the RestTemplate creates the request.

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 is created using the createRequest method. The createRequest method is a method of HttpAccessor. From the above class hierarchy diagram, it can be seen that RestTemplate inherits HttpAccessor. It obtains a request factory through getRequestFactory() to create a request client ClientHttpRequest instance.

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

Then we look at how to get the request factory, getRequestFactory() is implemented in InterceptingHttpAccessor, it inherits HttpAccessor, in fact, what RestTemplate really integrates is InterceptingHttpAccessor. The following code creates an instance of 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();
    }
}

The above code mainly creates an instance of the factory class. If the interceptor is not empty, an InterceptingClientHttpRequestFactory instance is created, otherwise a default instance is returned. The following is the default ClientHttpRequestFactory instance provided by Spring:

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

 In addition to the implementation of SimpleClientHttpRequestFactory, we can set the factory instance through restTemplate.setRequestFactory(requestFactory). The following is the implementation of ClientHttpRequestFactory:

 How to create an instance of ClientHttpRequest is not described here. The core is to create a client of an HTTP request and add some parameters required by the request. We focus on how to execute the HTTP request process. There is a very important interface here. The interceptor introduced: ClientHttpRequestInterceptor, it will intercept the request and execute the interceptor before the request is executed. Here we can do many things, such as adding request headers, changing the request URL, authorization, etc. Some of the code is as follows:

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

Because ClientHttpRequestInterceptor is traversed execution, in order to ensure the request execution, we must call the execute method above in our custom ClientHttpRequestInterceptor. When the interceptor is executed, the InterceptingRequestExecution instance has been passed in, so we can call it directly. Below we look at an authorization interceptor provided by Spring, the code is as follows:

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's load balancing is also implemented by implementing ClientHttpRequestInterceptor. If you are interested, you can refer to the content of the column microservice technology stack Spring Cloud Netflix load balancing component-Ribbon advanced applications .

 

Guess you like

Origin blog.csdn.net/wk19920726/article/details/108754574