环境: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就具备服务发现的能力了。