Each sentence
Happy Mid-Autumn Festival
Preface
Before reading this article, I suggest reading it first. Opening chapter The effect is better. RestTemplate is a client tool provided by Spring to access Rest services. It provides a variety of convenient methods to access remote Http services, which can greatly improve the efficiency of client writing.
For those who are still using HttpClient (or other Client) in Spring environment, after reading this article today, we suggest switching to RestTemplate.
RestTemplate simplifies communication with HTTP services, and program code can provide it with URLs and extract results. It uses JDK's HTTP URLConnection by default to communicate, but we can switch to different HTTP sources through RestTemplate.setRequestFactory: Apache HttpComponents, Netty, OkHttp, and so on.
RestOperations
Specify a set of interfaces for basic restful operations, define a basic set of Rest operations, whose only implementation is RestTemplate; not directly used, but this is a useful option to enhance testability because it is easy to emulate or stub (see the following sentence).
Referring to RedisOperations for comparison, it has only one implementation class, RedisTemplate. They both adopted the template pattern in the design pattern.
Methods:
Since there are so many methods in this interface (40 + ones), I categorize them according to Http standard as follows:
// @since 3.0 public enum HttpMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; ... }
HttpMethod | Method |
---|---|
GET | |
HEAD | |
POST | |
PUT | |
PATCH | |
DELETE | |
OPTIONS | |
TRACE | nothing |
Any (execute any Http method) |
Observation shows that although there are many methods, there are strong rules to follow. Each method has three overload implementations: two url parameters are strings and one URI parameter, so if you master the rules and then use them, you don't have to be afraid of how many of them are used.
xxxForObject: Return the response body (which is the body's physical strength directly) (T)
xxxForEntity: Returns the corresponding row, response header, response code, response body, etc. (ResponseEntity < T>)
xxxForLocation: After successful submission, return the URI of the new resource. This only requires the service provider to return a URI, which represents the location of the new resource and is very lightweight. (URI)
Note: By default, URLs that use string types are escaped, such as http://example.com/hotel list when executed, to http://example.com/hotel%20list. Implicit escaping is no problem. But if you've transferred yourself, that's not ok ay.
If you do not want this implicit escape, it is recommended to construct it using URI (URI uri = uriComponents.toUri()).
== Three ways of POST request in RestTemplate==
post
Request Representatives to Build New/Create a resource, so it has a return value. Because its use is the most complex, this paper takes it as an example to explain.If you've used the browser proficiently
Developer Tools
After debugging, you must know.POST
There are two ways to request it to pass parameters:
- Form Data mode: We use from The way the form is submitted is it; use it ajax(Note: This refers to jQuery Of ajax,Not from the source js The default submission method is also it~
- request payload mode: Multipart approach/json mode
These two ways are through
Content-Type
To distinguish: ifapplication/x-www-form-urlencoded
That isformdata
How; if soapplication/json
perhapsmultipart/form-data
Wait a minute. That's it.request payload
mode
jQuery
In execution post When requested, the default settings will be set for youContent-Type
byapplication/x-www-form-urlencoded
,So the server can parse correctly.
If used js Native ajax,If notDisplayed
Set up Content-Type,So the default is text/plain,At this point, the server does not know how to parse the data, so it can only parse the request data by acquiring the original data stream. I don't believe anybody did that.~)exchange and execute Method:
exchange Method: More general request method. It must accept one
RequestEntity
,This allows you to set the requested path, header, and so on, and ultimately all return one.ResponseEntity
(Can send Get,Post,Put Wait for all requests.
execute Method:The lowest and most generalRequest method.RequestCallback: Used to manipulate request headers and body,On request
Front
Execution; ResponseExtractor: analysis/extract HTTP Response data, and there is no need to worry about exceptions and resource closuresRequestCallback.doWithRequest(ClientHttpRequest)
To put it plainly is to get itClientHttpRequest
After that, he continued to be dealt with.~RestTemplate
OfacceptHeaderRequestCallback,httpEntityCallback
These methods can set it up~HttpAccessor,InterceptingHttpAccessor
These two abstract classes cannot be ignored.
HystrixCommand and Ribbon
The logic of the interceptor is related to it.HttpAccessor
Is an abstract base class that defines operationsClientHttpRequestFactory
Common attributes, which are generally not used directly.// @since 3.0 public abstract class HttpAccessor { // RestTemplate's default client factory: based on the native JDK private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); // To switch to the underlying components of a tripartite library, you can set this method public void setRequestFactory(ClientHttpRequestFactory requestFactory) { this.requestFactory = requestFactory; } ... // get method // Providing subclasses makes it easy to get a ClientHttpRequest protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { ClientHttpRequest request = getRequestFactory().createRequest(url, method); return request; } }
Its subclass is Intercepting Http Accessor, which is also an abstract implementation, mainly manages the interceptors of requests: ClientHttp Request Interceptor.
InterceptingHttpAccessor
// @since 3.0 // @see InterceptingClientHttpRequestFactory public abstract class InterceptingHttpAccessor extends HttpAccessor { // Loading interceptors that need to be used on RestTemplate~~~ private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); @Nullable private volatile ClientHttpRequestFactory interceptingRequestFactory; // The meaning here is set, so it's completely replaced. public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) { if (this.interceptors != interceptors) { this.interceptors.clear(); this.interceptors.addAll(interceptors); AnnotationAwareOrderComparator.sort(this.interceptors); } } // It's interesting to rewrite the parent class. // If the caller manually sets in, the factory set by the caller is the criterion, otherwise the Intercepting Client HttpRequestFactory is used. @Override public void setRequestFactory(ClientHttpRequestFactory requestFactory) { super.setRequestFactory(requestFactory); this.interceptingRequestFactory = null; } // If the interceptor is configured, the Intercepting Client HttpRequestFactory is used by default instead of SimpleClient HttpRequestFactory.~~~ @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(); } } }
The main processing logic of Intercepting Http Accessor is that if the caller is found to have set up a request interceptor, the factory it creates is the Intercepting Client Http Request Factory with interception function, otherwise it is the default SimpleClient Http Request Factory.
Intercepting Client HttpRequest Factory which produces Client HttpRequest is Intercepting Client HttpRequest, but it will execute the interceptor's interception method: nextInterceptor.intercept(request, body, this)
Question: If there are multiple request interceptors configured, will they be executed?
Answer: Don't be confused and jump to the conclusion that it's wrong to assume that there is no iterator.next() but only iterator.next() that if there are more than one, only one will be executed. In fact, there is an execution chain. As long as the intercept method of the intercept of the intercept of the intercept of the intercept of the intercept finally calls the intercept() method of the executor, the intercept chain will continue to execute. The fundamental reason for this is that the third parameter is passed in by the same executor (this=InterceptingRequestExecution)
==RestTemplate==
RestTemplate
UsesynchronizationMode execution HTTP The requested class, which is used by default at the bottomJDK
NativeHttpURLConnection API
. It implements interfacesRestOperations
,There are a lot of template methods (overload methods) that allow developers to send more easily HTTP Request.It should be noted that,
RestTemplate
yesSpring 3.0
Yes, but Spring5.0 Later, Spring Official recommendationorg.springframework.web.reactive.function.client.WebClient
Replace it, especially for asynchronous scenarios.
RestTemplate
Because it is widely used, so Even when it comes to Spring 5.0,Officials only suggest alternatives, but not labels.@Deprecated
,So at least for now, you can use it as you want.
howeverAsyncRestTemplate
It's clearly marked.@Deprecated
,It is strongly recommended to useorg.springframework.web.reactive.function.client.WebClient
To replace, so in 5.0 It is not recommended to use it again.~.Of course, there's one more point to make: if you don't use it in your project
WebFlux
The technology stack handles requests, so there's no need to say it's for use, so there's no need to import packages specifically for it (personal advice)~// @since 3.0 public class RestTemplate extends InterceptingHttpAccessor implements RestOperations { // De classpath to detect if there are jar s associated with these message converters~ // In general, we will guide Jackson 2Present.~~~ private static boolean romePresent; private static final boolean jaxb2Present; private static final boolean jackson2Present; private static final boolean jackson2XmlPresent; private static final boolean jackson2SmilePresent; private static final boolean jackson2CborPresent; private static final boolean gsonPresent; private static final boolean jsonbPresent; ... // The following four variables are important: // Message converters (apparently the best supported JSON format by default) private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); // The default request exception handler, after Spring 5.0, can actually use it to extract Response Error Handler // It can use message exchange to extract your wrong content. It also supports custom error codes, error sequences, and so on.~ private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); // Construction of URL s private UriTemplateHandler uriTemplateHandler; // Default Return Value Extractor~~~~ private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor(); // Empty constructs should be the most commonly used: everything uses default components to configure resources, and so on. public RestTemplate() { // These message converters are supported. Byte arrays, strings, this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); this.messageConverters.add(new SourceHttpMessageConverter<>()); // Support for form submission this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); // Then there are some columns of judgments that will only be added if there are classpaths. if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } ... // new DefaultUriBuilderFactory() this.uriTemplateHandler = initUriTemplateHandler(); } // You know, if you want to use OkHttp, you can also specify it at construction time. public RestTemplate(ClientHttpRequestFactory requestFactory) { this(); setRequestFactory(requestFactory); } // If you don't want to use the default message converter, you can also specify it by yourself (in fact, it's usually not done that way, but you can add it in later). public RestTemplate(List<HttpMessageConverter<?>> messageConverters) { Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); this.messageConverters.addAll(messageConverters); this.uriTemplateHandler = initUriTemplateHandler(); } ... // get/set offenders who omit the above attributes }
This part of the source code I listed is related to the preparation of building a RestTemplate instance, including the settings for various related components.
Next, more important is the interface method it implements. I draw out some key points to describe it.
RestTemplate: @Override @Nullable public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { //1. New AcceptHeader Request Callback (responseType) can do something like this before sending a request: // request.getHeaders().setAccept(allSupportedMediaTypes) RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); // The final call is the execute method, where the URL is a string // The responseExtractor Return Value Extractor uses a message converter to read the body clip.~ // The return value is the body itself returned (does not contain the return response header, etc.) return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } // It returns ResponseEntity, and the last call that does not return null is still the execute method. // Instead of using the extractor of the message converter, the internal class `ResponseEntityResponseExtractor'(bottom or dependent on the message converter) // But this extractor can extract all the ResponseEntity < T > instances.~ @Override public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables)); } // HEAD request: Simply, the extractor used is headers extractor, which takes the response header out of the return value. @Override public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException { return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables)); } // POST request @Override @Nullable public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException { // 1. HttpEntityRequestCallback adaptation: adapting request to a HttpEntity // Then, before execution, the header information, body information, etc. are written in through the message converter. RequestCallback requestCallback = httpEntityCallback(request); // Because you need to get the URI, you can use the headers extractor extractor here to get the response header first.~~~ HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return (headers != null ? headers.getLocation() : null); } // Except for httpEntityCallback(), the rest is the same as get requests. @Override @Nullable public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } // PUT request: Because there is no return value, there is no need for a return value extractor. So, it's very simple.~~~ @Override public void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } // DELETE request: Also wood has return value. // And please note: DELETE requests can not receive body here, and can not set the request body. // (Although the underlying httpCLient support may be available, it is not supported here. Please follow the specifications.) @Override public void delete(String url, Object... uriVariables) throws RestClientException { execute(url, HttpMethod.DELETE, null, null, uriVariables); } // OPTIONS requests: Almost the same processing logic as HEAD requests @Override public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException { ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return (headers != null ? headers.getAllow() : Collections.emptySet()); }
The logic of execution of all methods is basically the same, which is related to RequestCallback, responseExtractor and so on, and ultimately delegated to the lowest execute() method to execute.
Do you have any questions: the return values of the put method provided by it are all void, so what if I put the request has a return value swollen? Then I'll introduce a more general approach: exchange()
RestTemplate: @Override public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { // Adapt the requester to HttpEntity RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); // Message extractor uses ResponseEntityResponseExtractor ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); // From the above two parts, we can see that the exchange method is very common in both input and output parameters.~~~ return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); } // Parameterized Type Reference parameterized type for handling generics // The responseType above is a Class. Here is a parameterized type~~~~~ @Override public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException { Type type = responseType.getType(); RequestCallback requestCallback = httpEntityCallback(requestEntity, type); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables)); } // This method is very concise, allowing the caller to construct the RequestEntity itself, which contains information such as the URL and method of the request. @Override public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor)); }
All exchange methods use HttpEntity and ResponseEntity to represent requesting and responding entities, enough to see the versatility of its design.
After Spring 3.2, a Parameterized Type Reference is provided to handle parameterized types - > mainly for generic types such as List s.
It can be found that even the exchange() method is ultimately delegated to execute/doExecute for execution:
RestTemplate: // Three execute methods. The final call is the doExecute method // One thing it does: it uses UriTemplateHandler to fill in the parameters of the URL~~~ // The underlying use is the `UriComponents Builder', which I introduced earlier, or is relatively simple @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); } doExecute Method: @Nullable protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { ClientHttpResponse response = null; ClientHttpRequest request = createRequest(url, method); // If there is a callback, then call back first to process the request. if (requestCallback != null) { requestCallback.doWithRequest(request); } // Send requests in a real sense. // Note: If the request here is `Intercepting Client HttpRequest', then execute the intercept method of the interceptor.~~~ // When is Intercepting Client HttpRequest? Here's what's on it. response = request.execute(); // Processing results (throw exceptions if there are errors) handleResponse(url, method, response); // The request is normal. Then use the return value extractor responseExtractor to extract the content.~~~ return (responseExtractor != null ? responseExtractor.extractData(response) : null); ... // Close Response (ClientHttpResponse inherits the Closeable interface) finally { if (response != null) { response.close(); } } }
After looking at the template implementation steps of doExecute(), the complete process of RestTemplate from sending a request to receiving a response is clear. Spring designed a number of related components, providing hooks to allow us to intervene in the process, the most common of course is the request interceptor, which has a good application in Ribbon load balancing and Hystrix fuse.~
AsyncRestTemplate
It is a new scenario for @since 4.0 to solve some asynchronous Http requests, but it has a short lifetime. It is marked @Deprecated in Spring 5.0 and is recommended to replace it with WebClient.
Its basic principle is: RestTemplate + SimpleAsyncTaskExecutor task pool to achieve the asynchronous request, the return value is ListenableFuture. After mastering RestTemplate, there is no barrier to its use.
Minimalist use of Demo Show
Having read the description of the principle, I have reason to believe that you are thoroughly familiar with RestTemplate and can use it freely. So as for usage, this article only gives a very simple Demo Show as follows, which I think is enough:
public static void main(String[] args) throws IOException { RestTemplate restTemplate = new RestTemplate(); String pageHtml = restTemplate.getForObject("http://www.baidu.com", String.class); System.out.println(pageHtml); // Baidu Home Page html. }
Explanation: The request here is an html page, so when HttpMessageConverter Extractor extracts the response, it uses String HttpMessageConverter to process it. The extraction code is as follows:
StringHttpMessageConverter: @Override protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException { // Extract from the contentType of the response header (if application/json, the default is urf-8) // If no encoding is specified, the value getDefaultCharset is taken. For example, when we visit Baidu, we use the default value `ISO-8859-1'to encode body.~ Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType()); return StreamUtils.copyToString(inputMessage.getBody(), charset); }
Compare this request case with the one I sent with Client HttpRequestFactory above (or with your own HttpClient step), and feel how elegant RestTemplate is.~
Recommended reading
RestTemplate components: Client HttpRequestFactory, Client HttpRequest Interceptor, Response Extractor
Why does a @LoadBalanced annotation enable RestTemplate to have load balancing capabilities? [Enjoy Spring Cloud]
summary
Today, RestTemplate is a powerful tool for mainstream microservices, and every programmer should master it. Deep understanding of it is of great practical significance for practical application and optimization, so I believe this article can help you to be thoroughly familiar with it.
Preview: The next article will explain why a simple @LoadBalanced annotation enables RestTemplate to have load balancing capabilities.
== If you are interested in Spring, Spring Boot, MyBatis and other source code analysis, you can add me wx: fsx641385712, invite you to join the group and fly together manually.==
== If you are interested in Spring, Spring Boot, MyBatis and other source code analysis, you can add me wx: fsx641385712, invite you to join the group and fly together manually.==