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:
- RestTemplate inherited
InterceptingHttpAccessor
class - The RestTemplate class does
RestOperations
interface - 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:
- Create request object Request
- Execute remote call request.execute()
- 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.