弃用RestTemplate!RestClient真香

开发 前端
从Spring Cloud 4.1.0开始RestClient支持服务发现的负载均衡。在RestTemplate时代,我们在定义RestTemplate bean对象时,只要添加了@LoadBalanced注解,那么我们的RestTemplate就可以通过服务名的方式远程调用。

环境:Spring Boot3.2.5

1. 简介

从Spring Framework 6.1和Spring Boot 3.2开始,我们可以使用Spring       RestClient通过流畅且同步的API执行HTTP请求。RestClient基于底层的HTTP客户端库工作,如JDK       HttpClient、Apache HttpComponents等。

顾名思义,RestClient提供了WebClient的流畅API设计和RestTemplate的功能。RestClient在设计时就考虑了可测试性,使得在单元测试中模拟HTTP交互变得更加容易。

请注意,对于异步和流式处理场景,WebClient仍然是首选的API。

RestTemplate、RestClient 和 WebClient 如何选择?

从Spring 6.1开始,与RestTemplate相比,RestClient为同步HTTP访问提供了更现代的API。RestTemplate是Spring 3中引入的,它是一个臃肿的类,以模板类的形式暴露了HTTP的所有功能,但拥有过多的重载方法。

WebClient也支持同步HTTP访问,但它需要额外的依赖spring-boot-starter-webflux。而使用RestClient,我们可以避免在项目中添加新的依赖。

RestTemplate:是一个较旧的、用于发起HTTP请求的同步API。它缺乏新应用程序可能需要的灵活性和现代特性。

WebClient:是Spring WebFlux的反应式、非阻塞客户端部分。尽管它可以用于同步交互,但对于简单的用例来说,它似乎有些过于复杂。

RestClient:是Spring框架的新增成员,旨在取代RestTemplate。它提供了一个像WebClient一样更现代、流畅的API,但不需要反应式堆栈,因此在RestTemplate和WebClient之间找到了一个平衡点。

以下是RestTemplate与WebClient对比:

功能

WebClient

RestTemplate

反应式编程

基于反应式原理构建,支持反应式编程

同步,不是为反应式编程而设计的

技术

基于反应堆栈

基于 Servlet 栈

线程模型

使用非阻塞 I/O,适合处理大量并发请求

使用阻塞 I/O,在高并发情况下可能导致线程阻塞

Java版本

需要 Java 8 或更高版本。支持函数式编程

兼容 Java 6 或更高版本

错误处理

使用 onErrorResume、onErrorReturn 等操作符提供强大的错误处理功能

错误处理通常使用 try-catch 块进行

流式

使用 Flux 和 Mono 支持流式数据,适用于反应式流式应用场景

对流的支持有限,不适合反应式流

使用类

最适合微服务、反应式应用程序和需要高并发性的应用场景

适用于传统的单片式应用和简单用例

依赖

需要依赖 Spring WebFlux

需要 Spring Web 依赖关系

功能支持

与反应式编程模式相一致,并有可能得到持续发展和支持

可能会进行维护更新,但今后可能不会受到那么多关注

接下来,我将详细的介绍RestClient的使用。

2. 实战案例

2.1 创建RestClient

Spring 允许使用多种灵活的方法来初始化 RestClient Bean。例如,最简单的方法是使用 create() 方法。

@Value("${pack.remote.address:http://www.pack.com}")
private String baseURI;
@Bean
public RestClient restClient() {
  return RestClient.create(baseURI) ;
}

我们还可以使用 builder() 方法来设置更复杂的选项,如默认头、请求处理器、消息处理程序等。例如,下面的配置使用 HttpClient 作为 HTTP 连接管理的底层库。

@Bean
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
  HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
    = new HttpComponentsClientHttpRequestFactory() ;
  clientHttpRequestFactory.setHttpClient(httpClient) ;
  // 更多配置
  return clientHttpRequestFactory ;
}
@Bean
public RestClient restClient(CloseableHttpClient httpClient) {
  return RestClient.builder()
    .baseUrl(baseURI)
    // 更多配置
    .requestFactory(clientHttpRequestFactory())
    .build() ;
}

甚至,我们还可以直接通过RestTemplate来构建RestClient对象

@Bean
public RestClient restClient(RestTemplate restTemplate) {
  return RestClient.create(restTemplate);
}

2.2 HTTP Get请求

restClient.get() 用于向指定的 URL 创建 GET 请求。请注意,我们可以将动态值传递给 URI 模板。

@Resource
private RestClient restClient ;
restClient.get()
  .uri("/users")
  //...
restClient.get()
  .uri("/employees/{id}", id)
  //...

最后,retrieve() 方法发送请求并返回包含 API 响应的 ResponseSpec。下面的请求将获取用户列表,并将响应体解析为User实例列表。

List<User> list = restClient.get()
  .uri("/users")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .body(List.class) ;

我们还可以处理,如请求的状态、请求header等数据,可以按如下方式获取响应实体(ResponseEntity):

ResponseEntity<List> responseEntity = restClient.get()
  .uri("/users")
  .accept(MediaType.APPLICATION_JSON)
  .retrieve()
  .toEntity(List.class) ;

2.3 HTTP Post请求

restClient.post() 用于处理 POST 请求。除了 POST API 通常不返回任何响应外,其他大部分与 GET API 调用相同。toBodilessEntity() 方法具有完全相同的功能,可用于 POST API。

User user = new User(666L, "张三", 22) ;
ResponseEntity<Void> responseEntity = restClient.post()
  .uri("/users")
  .contentType(MediaType.APPLICATION_JSON)
  .body(user)
  .retrieve()
  .toBodilessEntity() ;

2.4 HTTP Put请求

restClient.put() 用于处理 PUT 请求。PUT API 通常会发送一个请求正文并接收一个响应正文,如下示例。

User user = new User(666L, "张三", 22) ;
ResponseEntity<User> responseEntity = restClient.put()
  .uri("/users/666")
  .contentType(MediaType.APPLICATION_JSON)
  .accept(MediaType.APPLICATION_JSON)
  .body(user)
  .retrieve()
  .toEntity(User.class) ;

2.5 HTTP Delete请求

restClient.delete() 用于处理 DELETE 请求。一般来说,delete API 在服务器中接受,并且不会要求响应体。

ResponseEntity<Employee> responseEntity = restClient.delete()
  .uri("/users/666")
  .retrieve()
  .toBodilessEntity() ;

2.6 RestClient复杂应用

如果我们需要完全控制响应处理,可以使用 exchange() 方法。它提供了对 HttpRequest 和 HttpResponse 对象的访问权限,然后我们就可以按自己的需要使用它们了。

List<Employee> list = restClient.get()
  .uri("/users")
  .accept(MediaType.APPLICATION_JSON)
  .exchange((request, response) -> {
    List response = null ;
    if (response.getStatusCode().is4xxClientError()
        || response.getStatusCode().is5xxServerError()) {
      System.err.println("请求错误") ;
    } else {
      response = new ObjectMapper().readValue(response.getBody(), List.class) ;
    }
    return response ;
  }) ;

2.7 异常处理

对于失败的请求,RestClient 会抛出两种异常:

HttpClientErrorException:带 4xx 响应代码

HttpServerErrorException:响应代码为 5xx

try {
  User user = restClient.get()
    .uri("/users/666")
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .body(User.class) ;
} catch(HttpClientErrorException e4xx) {
  // ...
} catch(HttpServerErrorException e5xx) {
  // ...
}

2.8 自定义拦截器

我们可以通过RestClient.Builder设置拦截器,通过拦截器我们可以进行日志的记录,认证的配置等等,如下示例:

@Bean
public RestClient demoRestClient(LoggingRestClientInterceptor loggingInterceptor) {
  return RestClient
    .builder()
    .requestInterceptor(loggingInterceptor)
    .build() ;
}

自定义拦截器

@Component
public static class LoggingRestClientInterceptor implements ClientHttpRequestInterceptor {
  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
      throws IOException {
    System.err.println("logging...") ;
    return execution.execute(request, body) ;
  }
}

注:RestTemplate与RestClient使用的拦截器是相同的。所以你可以复用之前写的拦截器。

2.9 基于服务发现负载均衡

从Spring Cloud 4.1.0开始RestClient支持服务发现的负载均衡。在RestTemplate时代,我们在定义RestTemplate bean对象时,只要添加了@LoadBalanced注解,那么我们的RestTemplate就可以通过服务名的方式远程调用。

RestClient要通过服务发现机制调用,那么我们就要自定义RestClient.Builder对象。

@Bean
@LoadBalanced
public RestClient.Builder demoRestClientBuilder() {
  Builder builder = RestClient.builder().baseUrl("http://demo") ;
  return builder ;
}

接下来,我们就可以通过上面自定义的RestClient.Builder来构建RestClient。

@Bean
public RestClient lbcRestClient(RestClient.Builder builder) {
  return builder
    .baseUrl("http://demo")
    .build() ;
}

这样配置,我们的RestClient就具备服务发现的能力了。

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2020-12-03 18:29:30

KubernetesDocker容器

2024-10-17 14:14:29

2011-03-23 09:40:37

Google GearChrome

2023-10-12 07:46:02

2022-04-08 16:14:21

FedoraBIOS32 位操作系统

2023-09-22 16:54:42

GNOME 45系统

2021-06-04 05:21:19

KubernetesDocker容器

2022-06-03 09:41:03

DockerKubernetes容器

2024-06-27 13:45:21

2011-08-05 09:59:04

GNOME 3Linus TorvaXfce

2020-12-18 09:23:41

KubernetesDocker

2020-10-28 07:03:11

NodeSassDart Sass

2010-05-20 14:37:34

红帽Xen

2020-05-15 08:30:25

前端开发工具

2018-09-10 15:40:46

GitHubQuery前端

2022-02-22 14:07:07

框架配置类Spring

2010-03-30 09:04:41

2010-01-13 09:49:34

微软windowsLinux

2019-01-18 12:05:44

MongoDB红帽开源

2021-03-25 14:41:05

TLS1.0IETF浏览器
点赞
收藏

51CTO技术栈公众号