一、初识RestTemplate
RestTemplate是Spring框架提供用于调用Rest接口的一个应用,它简化了与http服务通信方式。RestTemplate统一Restfull调用的标准,封装HTTP链接,只要需提供URL及返回值类型即可完成调用。相比传统的HttpClient与Okhttp,RestTemplate是一种优雅,简洁调用RESTfull服务的方式。
RestTemplate默认依赖JDK提供Http连接的能力(HttpURLConnection),如果有需要的话也可以通过SetRequestFactory方法替换为如:Apache HttpComponents、Netty或OKHttp等其他HTTP库。
二、RestTemplate调用流程详解:
2.1. 实例化RestTemplate
RestTemplate template=new RestTemplate();
2.2. 通过RestTemplate内部通过调用 doExecute 方法,首先就是获取 ClientHttpRequest
RestTemplate中doExecute核心代码
ClientHttpRequest request; try { request = createRequest(url, method); }catch (IOException ex) { ResourceAccessException exception = createResourceAccessException(url, method, ex); throw exception; }
if (requestCallback != null) { requestCallback.doWithRequest(request); }
response = request.execute();
handleResponse(url, method, response);
- handleResponse方法代码
ResponseErrorHandler errorHandler = getErrorHandler(); boolean hasError = errorHandler.hasError(response); if (logger.isDebugEnabled()) { try { HttpStatusCode statusCode = response.getStatusCode(); logger.debug("Response " + statusCode); }catch (IOException ex) { logger.debug("Failed to obtain response status code", ex); } } if (hasError) { errorHandler.handleError(url, method, response); }
2.3. RestTemplate 实现了抽象类 HttpAccessor ,可以调用父类(HttpAccessor)的 createRequest方法
在HttpAccessor中
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
public ClientHttpRequestFactory getRequestFactory() { return this.requestFactory; }
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { ClientHttpRequest request = getRequestFactory().createRequest(url, method); initialize(request); if (logger.isDebugEnabled()) { logger.debug("HTTP " + method.name() + " " + url); } return request; }
2.4. SimpleClientHttpRequestFactory 实现了接口ClientHttpRequestFactory,同时实现createRequest方法
在RestTemplate可以设置是否使用缓存流,默认设置:bufferRequestBody =true,缺点是当发送大量数据时,比如put/post的保存和修改,那么可能内存消耗严重。所以这时候可以设置 RestTemplate.setBufferRequestBody(false);
即使用 SimpleStreamingClientHttpRequest 来实现
@Override public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { //创建java.net.HttpURLConnection HttpURLConnection connection = openConnection(uri.toURL(), this.proxy); //创建connection属性,同时设置了setDoInput prepareConnection(connection, httpMethod.name()); if (this.bufferRequestBody) { return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming); } else { return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming); } }
2.5. openConnection 即打开连接,而是 prepareConnection 各种连接准备,针对请求者
openConnection源码:
protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException { URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection()); if (!(urlConnection instanceof HttpURLConnection)) { throw new IllegalStateException( "HttpURLConnection required for [" + url + "] but got: " + urlConnection); } return (HttpURLConnection) urlConnection; }
prepareConnection方法:
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { //设置连接超时时间:connectTimeout if (this.connectTimeout >= 0) { connection.setConnectTimeout(this.connectTimeout); } //设置读超时时间:readTimeout if (this.readTimeout >= 0) { connection.setReadTimeout(this.readTimeout); } Boolean mayWrite =("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)); //设置URL允许输入:connection.setDoInput(true);//默认true //URL连接可用于输入和/或输出。 如果您打算使用URL连接进行输入,请将DoInput标志设置为true; //否则,设置为false。 默认值是true。 //HttpUrlConnection中方法setDoInput(true);以后就可以使用conn.getInputStream().read(); connection.setDoInput(true); connection.setInstanceFollowRedirects("GET".equals(httpMethod)); //URL连接可用于输入和/或输出。 如果您打算将URL连接用于输出,请将DoOutput标志设置为true, //如果不是,则为false 默认值是false。 //如get请求,用不到conn.getOutputStream(),因为参数直接追加在地址后面,因此默认是false //post、put、patch、delete请求会将setDoOutput设置为true //设置setDoOutput(true);以后就可以使用conn.getOutputStream().write() connection.setDoOutput(mayWrite); connection.setRequestMethod(httpMethod); }