A brief
RestTemplate default constructor call, RestTemplate object creation request by using the HTTP implemented in java.net package at the bottom, an HTTP request can specify a different manner by using ClientHttpRequestFactory.
Using spring5.0.1
Default SimpleClientHttpRequestFactory, it is ClientHttpRequestFactory implementation class. The following process:
1) using a new instance of the default constructor
Rest Rest Template Template template = new ();
2) internal RestTemplate by calling doExecute method, first of all it is to get ClientHttpRequest
doExcute core processing
// produced by a plant ClientHttpRequestFactory ClientHttpRequest ClientHttpRequest Request = the this .createRequest (URL, Method); IF (! The requestCallback = null ) { // encapsulates the request header and request body, using HttpMessageConverter types requestCallback.doWithRequest (Request); } / / ClientHttpRequest & execution request execute Response = request.execute (); // Response error processing the this .handleResponse (URL, Method, Response); IF (responseExtractor == null ) { Resource = null ; return Resource; } var14 = responseExtractor.extractData(response);
Source
@Nullable protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { Assert.notNull(url, "'url' must not be null"); Assert.notNull(method, "'method' must not be null"); ClientHttpResponse response = null; Object var14; try { String resource; try { ClientHttpRequest request = this.createRequest(url, method); if(requestCallback != null) { requestCallback.doWithRequest(request); } response = request.execute(); this.handleResponse(url, method, response); if(responseExtractor == null) { resource = null; return resource; } var14 = responseExtractor.extractData(response); } catch (IOException var12) { 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; }
Here method calls ClientHttpRequest createRequest methods in HttpAccessor the third step.
3) RestTemplate implements the abstract class HttpAccessor, so you can call the parent class createRequest
In the HttpAccessor
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
public ClientHttpRequestFactory getRequestFactory() { return this.requestFactory; }
There is also provided a method of plant ClientHttpRequestFactory custom request, to the subsequent use
public void setRequestFactory(ClientHttpRequestFactory requestFactory) { Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null"); this.requestFactory = requestFactory; }
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { ClientHttpRequest request = this.getRequestFactory().createRequest(url, method); if(this.logger.isDebugEnabled()) { this.logger.debug("Created " + method.name() + " request for \"" + url + "\""); } return request; }
4) SimpleClientHttpRequestFactory achieved ClientHttpRequest, while achieving the method
Note bufferRequestBody is set in RestTemplate, it is a sign of whether to use a cached stream, the default is true, the disadvantage is that when sending large amounts of data, such as put / post preservation and modified, the memory consumption may be serious. So this time can be set RestTemplate.setBufferRequestBody (false);
That is implemented using SimpleStreamingClientHttpRequest.
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { HttpURLConnection connection = this.openConnection(uri.toURL(), this.proxy); this.prepareConnection(connection, httpMethod.name()); return (ClientHttpRequest)(this.bufferRequestBody?new SimpleBufferingClientHttpRequest(connection, this.outputStreaming):new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming)); }
The first line: Creating java.net.HttpURLConnection
Second row: create connection properties, and set up setDoInput
5) openConnection opens the connection, but is connected prepareConnection various preparations for the requestor
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { if(this.connectTimeout >= 0) { connection.setConnectTimeout(this.connectTimeout); } if(this.readTimeout >= 0) { connection.setReadTimeout(this.readTimeout); } connection.setDoInput(true); if("GET".equals(httpMethod)) { connection.setInstanceFollowRedirects(true); } else { connection.setInstanceFollowRedirects(false); } if(!"POST".equals(httpMethod) && !"PUT".equals(httpMethod) && !"PATCH".equals(httpMethod) && !"DELETE".equals(httpMethod)) { connection.setDoOutput(false); } else { connection.setDoOutput(true); } connection.setRequestMethod(httpMethod); }
5.1, set connection time: connectTimeout
5.2, set read timeout: readTimeout
5.3, set the URL to allow input: connection.setDoInput (true); // default true
URL connection can be used for input and / or output. If you plan to use the URL connection for input, then set DoInput flag is set to true; otherwise, set to false. The default value is true.
httpUrlConnection.setDoInput (true); later be used conn.getInputStream () read ().;
5.4、setDoOutput
URL connection can be used for input and / or output. If you intend to use the URL connection for output, set DoOutput flag is set to true, if it is not, and false default value is false.
If the request is get
get request less than conn.getOutputStream (), since additional parameters directly after the address, the default is false.
If a post, put, patch, delete
Several more requests will setDoOutput set to true.
Equivalent can be used: httpUrlConnection.setDoOutput (true); later be used conn.getOutputStream () write ().
6、执行requestCallback.doWithRequest(request);
RequestCallback request body and encapsulates the request header object, that is to say in which the object can get request parameters we need.
When performing doWithRequest, there is a very important step in front of him and Connection send the request body are closely related, we know that SimpleBufferingClientHttpRequest.addHeaders method request header, the request body bufferedOutput is how to assign it? In doWithRequest which is as follows StringHttpMessageConverter (other MessageConvert, too, there is often the cause garbled)
The above 8.1, it is found RequestCallback head and body for operating the request, request before execution. Here are two of the implementation class RequestCallback
AcceptHeaderRequestCallback | Only processing request header, for getXXX () methods. |
HttpEntityRequestCallback | AcceptHeaderRequestCallback request can be processed in succession and the head body, for putXXX (), postXXX () and exchange () method. |
Which HttpEntityRequestCallback inheritance: HttpEntityRequestCallback extends RestTemplate.AcceptHeaderRequestCallback
Therefore first check under AcceptHeaderRequestCallback Code
public void doWithRequest(ClientHttpRequest request) throws IOException { if(this.responseType != null) { Class<?> responseClass = null; if(this.responseType instanceof Class) { responseClass = (Class)this.responseType; } List<MediaType> allSupportedMediaTypes = new ArrayList(); Iterator var4 = RestTemplate.this.getMessageConverters().iterator();//第一步 while(var4.hasNext()) { HttpMessageConverter<?> converter = (HttpMessageConverter)var4.next(); if(responseClass != null) { if(converter.canRead(responseClass, (MediaType)null)) { allSupportedMediaTypes.addAll(this.getSupportedMediaTypes(converter)); } } else if(converter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter)converter; if(genericConverter.canRead(this.responseType, (Class)null, (MediaType)null)) { allSupportedMediaTypes.addAll(this.getSupportedMediaTypes(converter)); } } } if(!allSupportedMediaTypes.isEmpty()) { MediaType.sortBySpecificity(allSupportedMediaTypes); if(RestTemplate.this.logger.isDebugEnabled()) { RestTemplate.this.logger.debug("Setting request Accept header to " + allSupportedMediaTypes); } request.getHeaders().setAccept(allSupportedMediaTypes); } } }
Notes The first step: Get RestTemplate all MessageConverters binding.
There are above bound when RestTemplate five basic initialization, and much more, initialization code
public RestTemplate() { this.messageConverters = new ArrayList(); this.errorHandler = new DefaultResponseErrorHandler(); this.uriTemplateHandler = new DefaultUriBuilderFactory(); this.headersExtractor = new RestTemplate.HeadersExtractor(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); this.messageConverters.add(new SourceHttpMessageConverter()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if(romePresent) { this.messageConverters.add(new AtomFeedHttpMessageConverter()); this.messageConverters.add(new RssChannelHttpMessageConverter()); } if(jackson2XmlPresent) { this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); } else if(jaxb2Present) { this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if(jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } else if(gsonPresent) { this.messageConverters.add(new GsonHttpMessageConverter()); } else if(jsonbPresent) { this.messageConverters.add(new JsonbHttpMessageConverter()); } if(jackson2SmilePresent) { this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter()); } if(jackson2CborPresent) { this.messageConverters.add(new MappingJackson2CborHttpMessageConverter()); } }
At this point you can analyze the code StringHttpMessageConverter
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException { if(this.writeAcceptCharset) { outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets()); } Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType()); StreamUtils.copy(str, charset, outputMessage.getBody()); }
Str which is the body of the request; HttpOutputMessage ClientHttpRequest object is the object of our preparation, which is above SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest
Thus, the first call the parent class of the flow, the contents written to the stream, and then call the parent class in executeInternal method calls itself executeInternal this method, as the next step
Note Point 1: charset character set, may be from client;
public static final Charset DEFAULT_CHARSET; // static block of code, i.e. set ISO_8859_1, but network traffic is typically UTF-8, where it is most needed to set the encoding
static { DEFAULT_CHARSET = StandardCharsets.ISO_8859_1; }
Note point 2: StreamUtils spring is a class for processing stream, and is the outputStream inputStream java.io package, not java8 in Steam. It depends only on the use of spring-core
The copy method takes three parameters: the copied string, write the file specified character set specified destination (outputStream).
See more: https: //blog.csdn.net/neweastsun/article/details/79432763
7) is then performed response = request.execute ();
Call Interface ClientHttpRequest
public interface ClientHttpRequest extends HttpRequest, HttpOutputMessage { ClientHttpResponse execute() throws IOException; }
Example SimpleBufferingClientHttpRequest then request the package body and the request header
View executeInternal
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { addHeaders(this.connection, headers); if(HttpMethod.DELETE == this.getMethod() && bufferedOutput.length == 0) { this.connection.setDoOutput(false); } if(this.connection.getDoOutput() && this.outputStreaming) { this.connection.setFixedLengthStreamingMode(bufferedOutput.length); } this.connection.connect(); if(this.connection.getDoOutput()) { FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream()); } else { this.connection.getResponseCode(); } return new SimpleClientHttpResponse(this.connection); }
When the delete request through the request body and whether there are objects to determine whether to send the request body
If a delete request, the first set DoOutput = true, then the volume data according to whether there is a request, then the request package body
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
8) analytical response
Followed by parsing the response, mainly to resolve the Error.
this.handleResponse(url, method, response);
Internal processing error processing mostly resolved
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { ResponseErrorHandler errorHandler = this.getErrorHandler(); boolean hasError = errorHandler.hasError(response); if(this.logger.isDebugEnabled()) { try { this.logger.debug(method.name() + " request for \"" + url + "\" resulted in " + response.getRawStatusCode() + " (" + response.getStatusText() + ")" + (hasError?"; invoking error handler":"")); } catch (IOException var7) { ; } } if(hasError) { errorHandler.handleError(url, method, response); } }
See Address:
https://my.oschina.net/heweipo/blog/801653