文章目录
RestTemplate 访问 Web 资源
- Spring Boot 中没有自动配置 RestTemplate
- Spring Boot 提供了 RestTemplateBuilder
RestTemplateBuilder.build()
请求方式
GET 请求
- getForObject() / getForEntiry()
POST 请求
- postForObject() / postForObject()
PUT 请求
- put()
DELETE 请求
- delete()
请求示例
RestTemplate restTemplate = new RestTemplateBuilder().build();
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090/mapper/user/find/{userId}")
.encode(StandardCharsets.UTF_8)
.build(6);
ResponseEntity<UserEntity> result = restTemplate.getForEntity(uri, UserEntity.class);
log.info("statusCode:{}, statusCodeValue:{}, headers:{},body:{}", result.getStatusCode(), result.getStatusCodeValue(), result.getHeaders(), result.getBody());
对应的日志
15:40:14.378 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:9090/mapper/user/find/6
15:40:14.443 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json]
15:40:14.481 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
15:40:14.485 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [top.chenfu1201.mapper.entity.UserEntity]
15:48:45.402 [main] INFO top.chenfu1201.mapper.controller.UserController - statusCode:200 OK, statusCodeValue:200, headers:{Content-Type=[application/json;charset=UTF-8], Transfer-Encoding=[chunked], Date=[Tue, 05 May 2020 07:48:45 GMT]},body:UserEntity(userId=6, createTime=Fri Aug 16 20:16:10 CST 2019, updateTime=Fri Aug 16 20:41:47 CST 2019, email=2323@12.com, userName=韩小姐, age=null, gender=0)
RestTemplate 高阶用法
构造 URI
- UriComponentsBuilder
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090/mapper/user/queryAll")
.encode(StandardCharsets.UTF_8)
.build()
.toUri();
- ServletUriComponentsBuilder
- MvcUriComponentsBuilder
传递 HTTP Header
在企业开发中,我们经常会在 header 中携带一些校验信息,比如 token 。使用方式如下
- RestTemplate.exchange() 配合 RequestEntity<T> / ResponseEntiry<T>
示例
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090/mapper/user/find/{userId}")
.encode(StandardCharsets.UTF_8)
.build(6);
RequestEntity<Void> requestEntity = RequestEntity
.get(uri)
.header("token", "mytoken")
.build();
ResponseEntity<UserEntity> result = restTemplate.exchange(requestEntity, UserEntity.class);
log.info("statusCode:{}, statusCodeValue:{}, headers:{},body:{}", result.getStatusCode(), result.getStatusCodeValue(), result.getHeaders(), result.getBody());
类型转换
- JsonSerializer / JsonDeserializer
- @JsonComponent
解析泛型对象
- RestTemplate.exchange() 配合 ParameterizedTypeReference<T>
示例
ParameterizedTypeReference<List<UserEntity>> listPtr =
new ParameterizedTypeReference<List<UserEntity>>() {};
String url = "http://localhost:9090/mapper/user/queryAll";
ResponseEntity<List<UserEntity>> result = restTemplate.exchange(url, HttpMethod.GET, null, listPtr);
log.info("statusCode:{}, statusCodeValue:{}, headers:{}", result.getStatusCode(), result.getStatusCodeValue(), result.getHeaders());
result.getBody().forEach(u -> log.info("user:{}", u));
日志
16:17:24.351 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:9090/mapper/user/queryAll
16:17:24.425 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json]
16:17:24.524 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
16:17:24.533 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.util.List<top.chenfu1201.mapper.entity.UserEntity>]
16:17:24.659 [main] INFO top.chenfu1201.mapper.controller.UserController - statusCode:200 OK, statusCodeValue:200, headers:{Content-Type=[application/json;charset=UTF-8], Transfer-Encoding=[chunked], Date=[Tue, 05 May 2020 08:17:24 GMT]}
16:17:24.662 [main] INFO top.chenfu1201.mapper.controller.UserController - user:UserEntity(userId=6, createTime=Fri Aug 16 20:16:10 CST 2019, updateTime=Fri Aug 16 20:41:47 CST 2019, email=2323@12.com, userName=韩小姐, age=null, gender=0)
16:17:24.662 [main] INFO top.chenfu1201.mapper.controller.UserController - user:UserEntity(userId=7, createTime=Fri Aug 16 21:02:47 CST 2019, updateTime=null, email=null, userName=赵司令0, age=17, gender=1)
---略
错误示例
List<UserEntity> listPtr = new ArrayList<>();
String url = "http://localhost:9090/mapper/user/queryAll";
List result = restTemplate.getForObject(url, listPtr.getClass());
System.out.println(result.get(0).getClass());
日志
16:38:05.466 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:9090/mapper/user/queryAll
16:38:05.516 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json]
16:38:05.698 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
16:38:05.713 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.util.ArrayList<?>]
class java.util.LinkedHashMap
可以看到每个实体是用 LinkedHashMap 来封装的,所以 List 集合内的泛型一旦改成某个实体类型,就会报 ClassCastException !
定制 Rest Template
Rest Teamplate 支持的 HTTP 库
通用接口
- ClientHttpRequestFactory
默认实现
- SimpleClientHttpRequestFactory
第三方实现
Apache HttpComponents【以前的 Http Client】
- HttpComponentsClientHttpRequestFactory
Netty
- Netty4ClientHttpRequestFactory
OkHttp
- OkHttp3ClientHttpRequestFactory
优化底层请求策略
连接管理
- PoolingHttpClientConnectionManager
- KeepAlive 策略
使用 Apache HttpComponents 后的默认策略 DefaultConnectionKeepAliveStrategy
@Override
public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
Args.notNull(response, "HTTP response");
final HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
final HeaderElement he = it.nextElement();
final String param = he.getName();
final String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(final NumberFormatException ignore) {
}
}
}
return -1;
}
超时设置
- connectTimeout / readTimeout
SSL 校验
- 证书检查策略
自定义配置示例【使用 httpcomponents 】
package top.chenfu1201.mapper.config;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
@SpringBootConfiguration
public class RestTemplateConfig {
@Bean
public HttpComponentsClientHttpRequestFactory requestFactory() {
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.evictIdleConnections(30, TimeUnit.SECONDS)
.disableAutomaticRetries()
// 有 Keep-Alive 认里面的值,没有的话永久有效
.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return requestFactory;
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofMillis(100))
.setReadTimeout(Duration.ofMillis(500))
.requestFactory(this::requestFactory)
.build();
}
}