Spring Boot 中的 RestTemplate

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();
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值