前言
概述
RestTemplate 是 Spring 提供的用于访问Rest服务的客户端,RestTemplate 提供了多种便捷访问远程Http服务的方法,它简化了与 http 服务的通信方式,统一了 RESTful 的标准,封装了 http 链接, 我们只需要传入 url 及返回值类型即可。相较于之前常用的 HttpClient,RestTemplate 是一种更优雅的调用 RESTful 服务的方式。默认情况下,RestTemplate 默认依赖 jdk 的HTTP连接工具(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。
该模板类的主要切入点为以下几个方法(其根据HTTP的六个方法制定六个主要方法)
HTTPMethod | RestTemplate Method | 说明 |
---|---|---|
Post | postForLocation | POST 数据到一个URL,返回新创建资源的URL |
postForEntity | POST 数据到一个URL,返回包含一个对象的ResponseEntity | |
postForObject | POST 数据到一个URL,返回根据响应体匹配形成的对象 | |
Get | getForObject | 发送一个HTTP GET请求,返回的请求体将映射为一个对象 |
getForEntity | 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象 | |
Delete | delete | |
head | headForHeaders | |
put | put | |
any | exchange | |
execute | 所有的get、post、delete、put、options、head、exchange方法最终调用的都是excute方法 |
对外开放的接口
- [x] 在内部,RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息
RestTemplate是 spring 的一个 rest 客户端,在 spring-web 这个包下,spring boot的依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
RestTemplate只是对其它Rest客户端的一个封装,本身并没有自己的实现。在没有第三方依赖的情况下其默认实现是HttpURLConnection(集成了URLConnection),这是JDK自带的REST客户端实现。现在来看下其它最常用的几种客户端的引入
SimpleClientHttpRequestFactory(封装URLConnection)
HttpComponentsClientHttpRequestFactory(封装HttpClient)
OkHttp3ClientHttpRequestFactory(封装OKHttp)
其切换与使用也很简单,在pom中引入相应依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
初始化RestTemplate
在Config中的配置:
package org.dllwh.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
@Bean("urlConnection")
public RestTemplate urlConnectionRestTemplate() {
RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
return restTemplate;
}
@Bean("httpClient")
public RestTemplate httpClientRestTemplate() {
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
return restTemplate;
}
@Bean("OKHttp3")
public RestTemplate OKHttp3RestTemplate() {
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
return restTemplate;
}
}
使用的时候一般都会只选择其中的一种,所以上面的几种配置任选其一。这里仅仅只是演示说明
RestTemplate前先得做一些初始化处理,比如指定http客户端工厂类、设置超时时间、响应参数转换器等。以 HttpComponents 为例说明。
package org.dllwh.config;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
public class RestTemplateConfig {
/** 建立连接的超时时间 */
private static int connectTimeout = 20000;
/** 连接不够用的等待时间 */
private static int requestTimeout = 20000;
/** 每次请求等待返回的超时时间 */
private static int socketTimeout = 30000;
/** 每个主机最大连接数 */
private static int defaultMaxPerRoute = 100;
/** 最大连接数 */
private static int maxTotalConnections = 300;
@Bean
public RestTemplate buildRestTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
return restTemplate;
}
/**
* @方法描述:创建HTTP客户端工厂
* @return
*/
@Bean
public HttpComponentsClientHttpRequestFactory createFactory() {
// httpClient连接配置
SSLContextBuilder builder = new SSLContextBuilder();
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain, String authType) {
return true;
}
};
builder.loadTrustMaterial(null, acceptingTrustStrategy);
} catch (Exception e) {
log.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
}
SSLConnectionSocketFactory socketFactory = null;
try {
socketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
} catch (KeyManagementException | NoSuchAlgorithmException e) {
log.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
}
// 注册http和https请求
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory).build();
// 开始设置连接池
PoolingHttpClientConnectionManager phccm = new PoolingHttpClientConnectionManager(registry);
// 最大连接数
phccm.setMaxTotal(maxTotalConnections);
// 同路由并发数
phccm.setDefaultMaxPerRoute(defaultMaxPerRoute);
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setSSLSocketFactory(socketFactory);
httpClientBuilder.setConnectionManager(phccm);
httpClientBuilder.setConnectionManagerShared(true);
// 重试次数,默认是3次,没有开启
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));
// 保持长连接配置,需要在头添加Keep-Alive
httpClientBuilder.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE);
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36"));
headers.add(new BasicHeader("Connection", "keep-alive"));
httpClientBuilder.setDefaultHeaders(headers);
CloseableHttpClient httpClient = httpClientBuilder.build();
// httpClient连接配置,底层是配置RequestConfig
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient);
// 连接超时
factory.setConnectTimeout(connectTimeout);
// 数据读取超时时间,即SocketTimeout
factory.setReadTimeout(socketTimeout);
// 连接不够用的等待时间,不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
factory.setConnectionRequestTimeout(requestTimeout);
// 缓冲请求数据,默认值是true。通过POST或者PUT大量发送数据时,建议将此属性更改为false,以免耗尽内存。
factory.setBufferRequestBody(false);
return factory;
}
}
GET请求
getForEntity
getForEntity() 的返回值类型 ResponseEntity,通过源码可以看到它继承了 HttpEntity ,封装了返回的响应信息,包括 响应状态、响应头、响应体等,获取Http请求的全部信息。
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException;
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException;
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;
ResponseEntity response = restTemplate.getForEntity("http://localhost:8081/server",String.class);
response.getHeaders(); //响应头
response.getStatusCode(); //响应码
response.getBody(); //响应体,即前面的result
- [ ] 第1个参数:要调用的服务的地址
- [ ] 第2个参数:表示希望返回的body类型
- [ ] 第3个以及以后参数:表示请求参数
- 可以使用map来封装请求参数,并作为getForObject的第三个参数,同时修改url如下,map中的"1"会替换url中的{1},"2"会替换url中的{2}
Map map = new HashMap();
map.put("1", "hello");
map.put("2", "world");
String result = restTemplate.getForObject("http://localhost:8081/server?param1={1}¶m2={2}",
String.class, map);
- 也可以直接将要传递的值放到getForObject方法的参数结尾,数量不限,它会按顺序替换{1}和{2}
String result = restTemplate.getForObject("http://localhost:8081/server?param1={1}¶m2={2}",
String.class, "hello", "world");
getForObject
getForObject 和 getForEntity 用法几乎相同,实际上是对getForEntity函数的进一步封装,返回值返回的是 响应体,省去了我们 再去 getBody()。
POST请求
postForObject
postForObject指post请求,并返回一个Object对象
String response = restTemplate.postForObject("http://localhost:8081/server?param1={1}¶m2={2}",
null, String.class, "hello", "world");
- [ ] 第1个参数就是getForObject第1个参数。
- [ ] 第2个参数为null,实际上是HttpEntity
- [ ] 第3个参数就是getForObject第2个参数
- [ ] 第4个及以后的参数就是getForObject第3个及以后的参数
postForObject除了第2个参数为null,其它地方用法和getForObject是一模一样的。但是post请求传参通常不是写在url上实现的,而是放在请求体中。此时,就需要使用第2个参数来传参,同时可省略第4个参数的url传参
Map map = new HashMap();
map.put("param1", "hello");
map.put("param2", "world");
String response = restTemplate.postForObject("http://localhost:8081/server", map, String.class);
- [x] 注意,服务端端接收不同参数时,语法也有所不同
public String server(@RequestBody Map map,String param1,String param2)
postForEntity
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
<T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType)
throws RestClientException;
postForLocation
PUT请求
在RestTemplate中,PUT请求可以通过put方法调用,put方法的参数和前面介绍的postForEntity方法的参数基本一致,只是put方法没有返回值而已
@RequestMapping("put")
public void put() {
restTemplate.put("http://http://localhost:8081/server/put/?userName={1}", '独泪了无痕');
}
DELETE请求
delete请求我们可以通过delete方法调用来实现
@RequestMapping("delete")
public void delete() {
restTemplate.delete("http://localhost:8081/server/delete/{1}", 100);
}
Exchange请求
exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不同之处在于它可以指定请求的HTTP类型。
需要注意的一点是对于返回结果为204 no content,这种没有返回值的请求,RestTemplate会抛错,有需要的话可以使用httpClient的fluent
Excute请求
excute() 的用法与 exchange() 大同小异了,它同样可以指定不同的 HttpMethod,不同的是它返回的对象是响应体所映射成的对象 ,而不是 ResponseEntity 。