Spring RestTemplate终极使用归纳总结
前言
第一次看到这个RestTemplate
的时候,还是遥远的2017年,记得那年去广西亚信学习,然后从一个项目中看到了RestTemplate
,那个时候也就知道怎么用,也就有那么个大概的印象。后来从广西回来后,这几年基本上也就没有过多的写Java代码了,基本就是与C++和Lua为伴;而这些年基本也就不写代码了,更多的是看别人写代码,前段时间评审团队的代码时,发现在调用第三方提供的服务时,都是直接使用的HttpClient调用的,你要是使用HttpClient的话,也是没有任何问题的,关键是你自己封装的工具类还漏洞百出。后来经过评审,由于我们的整个项目都是基于Spring框架的,最后还是决定直接使用RestTemplate+HttpClient的方式调用第三方的服务。
说说RestTemplate
既然要使用RestTemplate
,然后自己又好几年没有写Java了,然后又手痒,就写篇文章总结下这个RestTemplate
,以及如何使用RestTemplate
。
RestTemplate
是Spring框架提供的一个在应用中调用其他Restful服务的一个工具,它简化了与Http服务的通信方式,统一了Restful的标准,对使用方来说,提供了非常友好的上层API接口。相较于上面说的HttpClient,RestTemplate
是一种更优雅的调用Restful服务的方式。所以说,好用才会用。
在RestTemplate
中包含以下几个部分:
HttpMessageConverter
:对象转换器;将请求对象转换为具体的数据格式输出,例如:Jaxb2RootElementHttpMessageConverterket
提供对xml格式的输入输出支持;可以参见这篇文章,通过实战搞懂HttpMessageConverter
;ClientHttpRequestFactory
:客户端请求工厂接口,可以通过使用ClientHttpRequestFactory
指定不同的HTTP请求方式;实现了该接口就都可以与RestTemplate
进行集成,后面会细说与不同的框架进行集成;默认是JDK的HttpURLConnection
;ResponseErrorHandler
:异常错误处理;可以参考这篇文章,看下如何自定义异常处理;ClientHttpRequestInterceptor
:请求拦截器
以后有机会,我们会再在源码的层面上来对上面的几个部分进行分析。下面来看下RestTemplate
类提供的一些主要API:
方法名 | 描述 |
---|---|
| 通过GET请求获得响应结果 |
| 通过GET请求获取 |
| 以HEAD请求资源返回所有响应头信息 |
| 通过POST请求创建资源,获得响应结果 |
| 更通用版本的请求处理方法,接受一个 |
| 最通用的执行HTTP请求的方法,上面所有方法都是基于 |
下面就开始通过实战的代码来熟悉一下RestTemplate
。
RestTemplate实战
说了这么多,最重要的还是需要落实在实战上面。下面通过一个基于Spring Boot的测试工程来演示如何使用RestTemplate
。
getForObject
使用示例:
// 不带请求参数
@GetMapping("/getForObject")
public String getForObject() throws JsonProcessingException {
BookConfigBean bookConfigBean = restTemplate.getForObject("http://127" +
".0.0.1:8080/test2", BookConfigBean.class);
log.info(bookConfigBean.toString());
return objectMapper.writeValueAsString(bookConfigBean);
}
// 携带请求参数
@GetMapping("/getForObject")
public String getForObject() throws JsonProcessingException {
Map<String, String> uriVars = new HashMap<>();
uriVars.put("name", "果冻想公众号");
uriVars.put("author", "Jelly");
BookConfigBean bookConfigBean = restTemplate.getForObject("http://127" +
".0.0.1:8080/test2?name={name}&author={author}",
BookConfigBean.class, uriVars);
log.info(bookConfigBean.toString());
return objectMapper.writeValueAsString(bookConfigBean);
}
getForEntity
使用示例:
@GetMapping("/getForEntity")
public String getForEntity() throws JsonProcessingException {
Map<String, String> uriVars = new HashMap<>();
uriVars.put("name", "果冻想公众号");
uriVars.put("author", "Jelly");
ResponseEntity<BookConfigBean> responseEntity = restTemplate.getForEntity("http://127" +
".0.0.1:8080/test2?name={name}&author={author}",
BookConfigBean.class, uriVars);
HttpStatus statusCode = responseEntity.getStatusCode();
log.info("statusCode:" + statusCode);
int httpCode = responseEntity.getStatusCodeValue();
log.info("statusCodeValue:" + httpCode);
HttpHeaders headers = responseEntity.getHeaders();
log.info("headers:" + headers);
BookConfigBean bookConfigBean = responseEntity.getBody();
return objectMapper.writeValueAsString(bookConfigBean);
}
postForObject
使用示例:
@GetMapping("/postForObject")
public String postForObject() throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("token", "xxxxxxxxxxxxx");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("name", "果冻想公众号");
map.add("author", "Jelly");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
BookConfigBean bookConfigBean = restTemplate.postForObject("http://127" +
".0.0.1:8080/test3", request, BookConfigBean.class);
return objectMapper.writeValueAsString(bookConfigBean);
}
postForEntity
使用示例:
@GetMapping("/postForObject")
public String postForObject() throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("token", "xxxxxxxxxxxxx");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("name", "果冻想公众号");
map.add("author", "Jelly");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<BookConfigBean> responseEntity =
this.restTemplate.postForEntity("http://127" +
".0.0.1:8080/test3", request, BookConfigBean.class);
HttpStatus statusCode = responseEntity.getStatusCode();
log.info("statusCode:" + statusCode);
int httpCode = responseEntity.getStatusCodeValue();
log.info("statusCodeValue:" + httpCode);
headers = responseEntity.getHeaders();
log.info("headers:" + headers);
BookConfigBean bookConfigBean = responseEntity.getBody();
return objectMapper.writeValueAsString(bookConfigBean);
}
exchange
使用示例:
// 第一种使用方式
@GetMapping("/exchange")
public String exchange() throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("token", "xxxxxxxxxxxxx");
HttpEntity<String> request =
new HttpEntity<String>(null, headers);
Map<String, String> uriVars = new HashMap<>();
uriVars.put("name", "果冻想公众号");
uriVars.put("author", "Jelly");
ResponseEntity<BookConfigBean> responseEntity =
restTemplate.exchange("http://127.0.0.1:8080/test2?name={name" +
"}&author={author}", HttpMethod.GET, request,
BookConfigBean.class, uriVars);
return objectMapper.writeValueAsString(responseEntity.getBody());
}
// 第二种使用方式
@GetMapping("/exchange")
public String exchange() throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("token", "xxxxxxxxxxxxx");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("name", "果冻想公众号");
map.add("author", "Jelly");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<BookConfigBean> responseEntity =
restTemplate.exchange("http://127.0.0.1:8080/test3",
HttpMethod.POST, request,
BookConfigBean.class);
return objectMapper.writeValueAsString(responseEntity.getBody());
}
// 第三种使用方式
@GetMapping("/exchange")
public String exchange() throws JsonProcessingException, URISyntaxException {
HttpHeaders headers = new HttpHeaders();
headers.add("token", "xxxxxxxxxxxxx");
RequestEntity requestEntity = RequestEntity.get(new URI("http://127.0" +
".0.1:8080/test2")).headers(headers).build();
ResponseEntity<BookConfigBean> responseEntity = this.restTemplate.exchange(requestEntity, BookConfigBean.class);
BookConfigBean bookConfigBean = responseEntity.getBody();
return objectMapper.writeValueAsString(bookConfigBean);
}
// 第四种使用方式
@GetMapping("/exchange")
public String exchange() throws JsonProcessingException, URISyntaxException {
HttpHeaders headers = new HttpHeaders();
headers.add("token", "xxxxxxxxxxxxx");
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("name", "果冻想公众号");
map.add("author", "Jelly");
RequestEntity requestEntity = RequestEntity.post(new URI("http://127" +
".0.0.1:8080/test3")).headers(headers).body(map);
ResponseEntity<BookConfigBean> responseEntity = this.restTemplate.exchange(requestEntity, BookConfigBean.class);
BookConfigBean bookConfigBean = responseEntity.getBody();
return objectMapper.writeValueAsString(bookConfigBean);
}
异常处理
前面也简单说了一下异常处理,在Spring中对于RestTemplate
提供了一个默认的异常处理,我们在自定义异常处理时建议继承该默认继承类,例如这样:
public class CustomErrorHandler extends DefaultResponseErrorHandler {
private final Logger log = LoggerFactory.getLogger(CustomErrorHandler.class);
public void handleError(ClientHttpResponse response) throws IOException {
// todo
log.error("自定义异常处理");
}
}
在构建RestTemplate
实例的时候,需要设置异常处理:
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new CustomErrorHandler());
return restTemplate;
}
RestTemplate与OkHttp集成
上面也说到RestTemplate
默认是与JDK的HttpURLConnection
进行的集成,但是在实际生产项目中,HttpURLConnection
能力不够强大,无法满足我们的要求。而RestTemplate本身又具备与实现了ClientHttpRequestFactory
接口的Http Client类库进行集成,在实际工作中,最多的就是RestTemplate与OkHttp集成和RestTemplate与HttpClient集成,这里就重点把这两种集成方式进行总结。
至于OkHttp的牛叉之处,我这里就不再叙述了,因为好用,我这里才进行总结。
要使用OkHttp,首先要添加以下Maven依赖:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
接下来,我们对OkHttp3进行配置:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory okHttp3clientHttpRequestFactory() {
OkHttpClient httpClient = new OkHttpClient.Builder()
//.sslSocketFactory(sslSocketFactory(), x509TrustManager())
.retryOnConnectionFailure(false)
.connectionPool(pool())
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.build();
return new OkHttp3ClientHttpRequestFactory(httpClient);
}
@Bean
public X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory sslSocketFactory() {
try {
//信任任何链接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return null;
}
@Bean
public ConnectionPool pool() {
return new ConnectionPool(200, 5, TimeUnit.MINUTES);
}
}
RestTemplate与HttpClient集成
对于HttpClient,首先要添加以下Maven依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
接下来,我们对HttpClient进行配置:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory httpComponentsClientHttpRequestFactory(){
//Httpclient连接池,长连接保持30秒
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
//设置总连接数
connectionManager.setMaxTotal(1000);
//设置同路由的并发数
connectionManager.setDefaultMaxPerRoute(1000);
//设置header
List<Header> headers = new ArrayList<Header>();
headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04"));
headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate"));
headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"));
headers.add(new BasicHeader("Connection", "keep-alive"));
//创建HttpClient
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.setDefaultHeaders(headers)
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接
.build();
//创建HttpComponentsClientHttpRequestFactory实例
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
//设置客户端和服务端建立连接的超时时间
requestFactory.setConnectTimeout(5000);
//设置客户端从服务端读取数据的超时时间
requestFactory.setReadTimeout(5000);
//设置从连接池获取连接的超时时间,不宜过长
requestFactory.setConnectionRequestTimeout(200);
//缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存
requestFactory.setBufferRequestBody(false);
return requestFactory;
}
}
总结
各种找资料,编码验证,用了好久,终于搞定这篇文章,这篇文章里面最大的就是用代码说话,文中的所有代码都经过测试,可以直接复制使用。总结了这篇文章,以后在使用RestTemplate
的时候会更加从容和得心应手。
2020年11月7日 于呼和浩特。
人生是个圆,有的人走了一辈子也没有走出命运画出的圆圈,其实,圆上的每一个点都有一条腾飞的切线。
玩代码、玩技术
长按识别二维码,关注“果冻想”
如果觉得还不错,可以点个“在看”哦~
![](https://images.cnblogs.com/cnblogs_com/vipygd/1768905/o_200519134052%E5%8D%9A%E5%AE%A2%E7%94%A8%E5%9B%BE.png)