This is the 22nd day of my participation in the August More Text Challenge

Recently, I came into contact with the use of RestTemplate in my work project. As an inexperienced person with no knowledge of business, I was under great pressure from my colleagues’ business codes. After I understood the business, I found that RestTemplate was the core of the business. So I went to learn Spring’s RestTemplate, and today I will record my understanding of RestTemplate remote calls.

In Springboot, we used RestTemplate to create a RESTful service interface, and then used RestTemplate to access the RESTful interface. SpringBoot implements RESTful service interfaces.

RestTemplate remote call implementation principle

RestTemplate is a client template tool class provided by SpringBoot for accessing RESTful services.

RestTemplate class

Last time we talked about the simplest and most efficient way to create a utility class object when using RestTemplate, for example: new RestTemplate(); So let’s dive into the RestTemplate class definition this time to see how it is implemented.

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {...public RestTemplate(a) {
        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) { ; }}}... }Copy the code

As you can see from the source definition of the RestTemplate class:

  1. RestTemplate inheritedInterceptingHttpAccessorclass
  2. The RestTemplate class doesRestOperationsinterface
  3. Many converters are created in the no-parameter initialization method of the RestTemplate class

RestOperations interface

Let’s first look at the RestOperations interface definition:

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, @NullableHttpEntity<? > requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException; ... }Copy the code

You can see that the GET, POST, PUT, and delete methods provided by RestTemplate are defined in the RestOperations interface and follow the RESTful style. Finally, the related logic is implemented in the RestTemplate class.

InterceptingHttpAccessor abstract class

As you can see from the name, this class is an interceptor-related class, whose source code is defined as follows:

public abstract class InterceptingHttpAccessor extends HttpAccessor {
    private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
    private volatileClientHttpRequestFactory interceptingRequestFactory; . }Copy the code

We see in InterceptingHttpAccessor type definition contains two variables defined, one of the interceptors is responsible for set up and manage request interceptor ClientHttpRequestInterceptor collection, The other is responsible for getting the ClientHttpRequestFactory class used to create client HTTP requests.

In addition, the InterceptingHttpAccessor interceptor class, as an abstract class, itself inherits from the HttpAccessor abstract class.

HttpAccessor abstract class

HttpAccessor as a top-level abstract class, the source code is defined as:

public abstract class HttpAccessor {
    private ClientHttpRequestFactory requestFactory = newSimpleClientHttpRequestFactory(); ... }Copy the code

Its internal definition and instantiate the ClientHttpRequestFactory object, instantiation is through its implementation class SimpleClientHttpRequestFactory created.

Inheritance implementation diagram

From the logic in the source code definition, we can easily see the hierarchical inheritance relationship between them.

From the diagram analysis, we can see that the left side of RestTemplate class layer inherits HttpAccessor abstract class and InterceptingHttpAccessor abstract class, so as to complete the implementation mechanism related to HTTP requests. The right-hand side implements the RestOperations interface based on RESTful style and implements the methods defined in the interface. RestTemplate uses an object-oriented mechanism that implements interfaces and inherits abstract classes to achieve the aggregation of two parts of functionality.

RestTemlpate Internal execution flow

Now that you’ve learned the RestTemlpate class definition, let’s look at how the methods defined in the RestTemlpate class are implemented. As we mentioned last time, RestTemlpate has methods like GET, POST, PUT, DELETE, exchange, and execute, and all methods are ultimately executed through the execute method.

@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);
        returnheaders ! =null ? headers.getLocation() : null;
    }
Copy the code

Therefore, our core is the execute method. Next, we will mainly explore the execution process of the Execute method.

Execute method definition

@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);
}
Copy the code

As you can see, the execute method first builds a URI through UriTemplateHandler, and then delegates the request process to the doExecute method, which seems pretty deep. So let’s go back to the doExecute method

@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 {
        // Create the request object
        ClientHttpRequest request = this.createRequest(url, method);
        if(requestCallback ! =null) {
            requestCallback.doWithRequest(request);
        }
        
        // Remote call
        response = request.execute();
        
        // Extract the return result
        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;
}
Copy the code

As can be seen from the method flow, the whole remote call flow is mainly divided into three steps:

  1. Create request object Request
  2. Execute remote call request.execute()
  3. Return request result

Create the request object

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;
}
Copy the code

Create ClientHttpRequest process is a kind of typical plant model application scenario, create a SimpleClientHttpRequestFactory object implementing the ClientHttpRequestFactory interface, The client request object ClientHttpRequest is then created using the createRequest method of this object and returned to the upper component for use.

Perform the requested

Request that created earlier SimpleBufferingClientHttpRequest class, while the execute method is defined as:

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

Return request result

The final step in HTTP request processing is to read the input stream from the ClientHttpResponse, format it into a response body, and convert it into a business object. Let’s look at the handleResponse method that gets the returned result:

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); }}Copy the code

A ResponseErrorHandler is obtained using the getErrorHandler method. If the status code of the response is wrong, handleError can be called to handle the error and throw an exception. And the job of getting the response data and doing the transformation is in ResponseExtractor, in the RestTemplate class, We define a ResponseEntityResponseExtractor internal class implements the ResponseExtractor interface, as shown in the following code:

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 {
                returnResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build(); }}}Copy the code

Code ResponseEntityResponseExtractor extractData method is essentially the data extraction of part of the work entrusted to the delegate object an agent, This is the type of the delegate HttpMessageConverterExtractor, HttpMessageConverterExtractor class used within the HttpMessageConverter the transition of the message.

Finally, the complete process of initiating, executing, and responding to the entire HTTP request through the RestTemplate is covered.