1 RestClient使用
RestClient
是 Spring 6.1 M2 中引入的同步 HTTP 客户端,它取代了 RestTemplate
。同步 HTTP 客户端以阻塞方式发送和接收 HTTP 请求和响应,这意味着它会等待每个请求完成后才继续下一个请求。本文将带你了解 RestClient
的功能以及它与 RestTemplate
的比较。
1.1 RestClient
和 RestTemplate
RestTemplate
,顾名思义,是基于模板设计模式构建的。它是一种行为设计模式,用于在方法中定义算法的框架,允许子类为某些步骤提供特定的实现。虽然这是一种强大的模式,但它会导致需要进行方法覆写,这可能是不方便的。
为了改进这一点,RestClient
采用了 Fluent API。Fluent API 也是一种设计模式,它允许以一种使代码更易读和表达的方式进行方法链式调用,通常无需使用中间变量。从创建一个基本的 RestClient
开始:
RestClient restClient = RestClient.create();
1.2 使用各种 HTTP 方法发起请求
与 RestTemplate
或其他 REST 客户端类似,RestClient
可以使用不同的 HTTP 方法发起请求。创建一个用于操作的 Article
类:
java
public class Article {
Integer id;
String title;
// 构造函数和 getter/setter
}
1.2.1 使用 GET 请求获取资源
GET 请求用于检索 Web 服务器上指定资源的数据,而不对其进行修改。通常,这是一个只读的操作!获取服务器响应的字符串(String
):
java
String result = restClient.get()
.uri(uriBase + "/articles")
.retrieve()
.body(String.class);
1.2.2 使用 POST 请求创建资源
POST 请求用于提交数据到 Web 服务器上,进行新的资源创建。资源通常是通过 URI 定义的。向服务器发送一篇 ID 为 1
的 Article
:
java
Article article = new Article(1, "How to use RestClient");
ResponseEntity<Void> response = restClient.post()
.uri(uriBase + "/articles")
.contentType(APPLICATION_JSON)
.body(article)
.retrieve()
.toBodilessEntity();
由于指定了 APPLICATION_JSON
Content Type,Jackson 库会自动将 Article
类实例序列化为 JSON。在本例中,使用 toBodilessEntity()
方法忽略了响应体。POST 端点不需要返回任何 Payload,通常也不会返回任何 Payload。
1.2.3 使用 PUT 请求更新资源
接下来是 PUT 请求,用于更新或替换已经存在的资源。修改上一段中创建的 Article
,URI 中的 ID 即表示要修改的资源:
java
Article article = new Article(1, "How to use RestClient even better");
ResponseEntity<Void> response = restClient.put()
.uri(uriBase + "/articles/1")
.contentType(APPLICATION_JSON)
.body(article)
.retrieve()
.toBodilessEntity();
与 POST 请求一样,依靠 RestClient
对 Payload 进行序列化,并忽略响应。
1.2.4 使用 DELETE 请求删除资源
使用 DELETE 请求从 Web 服务器上删除指定资源。与 GET 类似,通常不提供任何 Payload,而是在 URI 参数中指定要删除的资源:
java
ResponseEntity<Void> response = restClient.delete()
.uri(uriBase + "/articles/1")
.retrieve()
.toBodilessEntity();
1.3 反序列化响应
通常,我们希望将请求序列化,并将响应反序列化为我们可以操作的类。RestClient
具有执行 JSON 到对象转换的功能,该功能由 Jackson
库提供。此外,由于共享使用了 Message Converter,因此可以使用 RestTemplate
支持的所有数据类型。通过 ID 获取一篇文章,并将其序列化为 Article
类的实例:
java
Article article = restClient.get()
.uri(uriBase + "/articles/1")
.retrieve()
.body(Article.class);
当我们需要获取 List
等泛型类的实例时,指定 body 的类就比较复杂了。例如,如果我们想获取所有 Article
,以 List<Article>
对象形式返回。在这种情况下,可以使用 ParameterizedTypeReference
抽象类来告诉 RestClient
我们要获取什么对象。甚至不需要指定泛型类型,Java 会自动推断出类型:
java
List<Article> articles = restClient.get()
.uri(uriBase + "/articles")
.retrieve()
.body(new ParameterizedTypeReference<>() {});
1.4 使用 Exchange 解析响应
RestClient
包含 exchange()
方法。通过提供对底层 HTTP 请求和响应的访问权限,用于处理更复杂的情况。在这种情况下,库不会应用默认的 Handler,我们必须自己处理响应的 Status。
比方说,当数据库中没有文章时,服务会返回 204
(No Content)状态代码。由于这种行为略显非标准,我们希望以一种特殊的方式来处理它。当状态代码等于 204
时,抛出一个 ArticleNotFoundException
异常;当状态代码不等于 200 时,抛出一个更通用的异常:
java
List<Article> article = restClient.get()
.uri(uriBase + "/articles")
.exchange((request, response) -> {
if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) {
throw new ArticleNotFoundException();
} else if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
return objectMapper.readValue(response.getBody(), new TypeReference<>() {});
} else {
throw new InvalidArticleResponseException();
}
});
由于我们在这里处理的是原始响应,因此还需要使用 ObjectMapper
对 Response Body 进行反序列化。
1.5 异常处理
默认情况下,当 RestClient
在 HTTP 响应中遇到 4xx
或 5xx
状态码时,它会抛出一个 RestClientException
子类的异常。我们可以通过自定义 Status Handler 来覆盖这一行为。自定义 Status Handler,在找不到文章时抛出一个自定义异常:
java
Article article = restClient.get()
.uri(uriBase + "/articles/1234")
.retrieve()
.onStatus(status -> status.value() == 404, (request, response) -> {
throw new ArticleNotFoundException(response)
})
.body(Article.class);
1.6 从 RestTemplate 构建 RestClient
如果你已经在项目中使用上了 RestTemplate
,也可以可从 RestTemplate
构建 RestClient
。
java
RestTemplate oldRestTemplate;
RestClient restClient = RestClient.create(oldRestTemplate);
2 HTTP 客户端区别
2.1 RestTemplate
RestTemplate
是 Spring 框架提供的同步 HTTP
客户端,专用于发起 RESTful
请求。它通过封装 HTTP
请求和响应的样板代码,提供了一种简洁的方式与 REST
服务进行交互。
2.1.1 核心特性
- 同步API: RestTemplate以同步方式运行,阻塞当前线程直到收到响应。
- 模板方法: 提供了用于常见HTTP操作(GET、POST、PUT、DELETE等)的预定义方法。
- 可定制性: 允许通过拦截器、错误处理程序和消息转换器进行定制。
- 对象映射: 可以使用消息转换器自动将请求和响应体转换为Java对象。
2.1.2 优点和缺点
优点:
- 使用简单易懂:
RestTemplate
提供了直观的 API,简化了与 RESTful 服务的交互,开发者可以轻松上手。 - 适用于简单用例: 对于常见的 CRUD 操作和简单的 HTTP 请求处理,
RestTemplate
是一种方便的选择。 - 与 Spring 生态系统良好集成: 作为 Spring 框架的一部分,
RestTemplate
与 Spring 的其他组件紧密配合,能够无缝融入到 Spring 应用中。
缺点:
- 同步特性可能在高并发场景中影响性能: 由于
RestTemplate
是同步的,调用线程会阻塞直到响应返回,这在高并发情况下可能导致性能瓶颈。 - 对异步和响应式编程的支持有限:
RestTemplate
不支持异步处理,也无法与响应式编程模式兼容,这使得它在需要非阻塞操作的场景中不够灵活。 - 正在被
WebClient
取代: 随着 Spring 5 的推出,WebClient
作为更现代的 HTTP 客户端出现,逐渐成为RestTemplate
的替代品,尤其是在响应式编程场景下。
2.1.3 使用 RestTemplate 的时机
RestTemplate
适用于那些同步行为可接受且性能要求不高的简单用例。对于需要进行直接 REST 交互的中小型应用程序,RestTemplate
可能是一个理想的选择。特别是在以下情况下,它是不错的工具:
- 简单的 CRUD 操作: 当应用程序主要处理创建、读取、更新和删除等基本 REST 操作时,
RestTemplate
提供了简洁易用的 API。 - 同步请求: 如果应用程序可以容忍同步请求,且并发需求不高,那么
RestTemplate
可以很好地满足需求。 - 快速原型开发: 在开发阶段,
RestTemplate
可以快速帮助构建和测试 RESTful API 的交互逻辑。
然而,当需要处理复杂的异步操作、流式处理或者更高的并发需求时,应该考虑使用 WebClient
或 RestClient
。
2.2 WebClient
WebClient
是在 Spring 5 中引入的非阻塞、响应式 HTTP 客户端,被设计为 RestTemplate
的替代品。它基于 Project Reactor
构建,支持异步和非阻塞操作。这种方式非常适合构建现代、可扩展的应用程序,特别是在需要高效处理高并发场景时,WebClient
能够显著提高性能和系统的响应能力。
WebClient
提供了灵活且强大的 API,使开发者能够轻松处理复杂的 HTTP 交互,包括流式数据处理和异步操作。由于它与 Spring 的响应式编程模型紧密集成,因此对于现代微服务架构和高并发应用来说,WebClient
是一个至关重要的工具。
2.2.1 核心特性
- 响应式 API:
WebClient
利用Mono
和Flux
等响应式编程概念来进行异步操作,使开发者可以轻松处理单个或多个异步数据流。 - 流畅接口:
WebClient
提供了声明式、可读性强的 API,用于构建和执行 HTTP 请求,开发者可以通过链式调用方式轻松定义复杂的请求逻辑。 - 非阻塞:
WebClient
的非阻塞特性允许在不阻塞当前线程的情况下发起请求,从而提高了资源利用率,特别是在高并发场景下表现尤为出色。 - 与 Spring 生态系统集成:
WebClient
与Spring Data Reactive
、Spring Security
等其他 Spring 组件无缝集成,支持响应式数据访问和安全认证等功能,使得它能够完美融入 Spring 响应式堆栈。
2.2.2 优点和缺点
优点:
- 非阻塞和异步:
WebClient
支持非阻塞的异步操作,可以更高效地利用系统资源,特别是在处理大量并发请求时表现出色。 - 并发请求的高性能: 通过异步处理和非阻塞 I/O,
WebClient
能够处理更高的并发请求量,适合需要高吞吐量的应用场景。 - 支持响应式编程:
WebClient
与 Spring 的响应式编程模型紧密集成,支持Mono
和Flux
,能够更自然地处理流式数据和异步操作。 - 更适合现代应用程序架构: 对于微服务架构、响应式系统等现代架构,
WebClient
提供了更丰富的功能和更高的灵活性,能够适应复杂的需求。
缺点:
- 学习曲线更陡峭: 由于
WebClient
基于响应式编程模型,开发者可能需要掌握Mono
和Flux
等响应式编程概念,这对不熟悉响应式编程的开发者来说会有一定的学习难度。 - 更复杂的错误处理: 与
RestTemplate
相比,WebClient
的错误处理可能更复杂,需要开发者在处理响应状态、异常和重试机制时更加小心和全面。
2.2.3 使用WebClient的时机
WebClient
是大多数现代 Spring 应用程序的首选工具。它非常适合微服务架构、高流量应用程序,以及那些非阻塞行为至关重要的场景。在构建响应式系统或需要高效处理大量并发请求时,WebClient
是一个值得优先考虑的选择。无论是处理复杂的 HTTP 交互,还是构建高度可扩展的系统,WebClient
都能为开发者提供强大的功能和灵活性,使其能够轻松应对现代应用程序的需求。
2.3 RestClient
RestClient
是 Spring HTTP 客户端领域的最新发展,作为 RestTemplate
的现代化和高效替代品引入。它旨在解决 RestTemplate
的一些局限性,同时结合了 WebClient
的最佳实践。
RestClient
提供了更先进的功能,专注于优化性能和提高开发效率,尤其在处理 RESTful API 请求时,结合了响应式编程和非阻塞操作的优点。通过引入 RestClient
,Spring 提供了一种更现代的解决方案,能够更好地满足当前和未来的应用需求。
2.3.1 核心特性和改进
- 构建者模式:
RestClient
使用构建者模式来构建请求,这种模式提供了更流畅、可读性更高的 API,使得请求的配置和管理更加直观和灵活。 - 异步操作: 尽管
RestClient
的异步能力不如WebClient
那么全面,但它通过CompletableFuture
提供了一定程度的非阻塞能力,适合需要异步处理但不要求完全响应式的场景。 - 响应式支持:
RestClient
可以与Project Reactor
等响应式编程框架集成,支持响应式编程模型,使其能够更好地适应现代应用程序的需求,并提高应用程序的可扩展性。 - 简化的错误处理:
RestClient
提供了改进的错误处理机制,使得异常处理和 HTTP 状态码的管理变得更加简单和直接,有助于提高开发效率和代码质量。
2.3.2 使用RestClient的时机
当我们需要在 RestTemplate
的简单性和 WebClient
的高级特性之间取得平衡时,RestClient
是一个合适的选择。它适用于那些需要异步操作但不需要完全响应式编程的项目,提供了一种折衷方案。
此外,如果您正在从 RestTemplate
迁移并希望逐步过渡,RestClient
也是一个很好的选择。它不仅保留了 RestTemplate
的易用性,还引入了现代化的特性,使得迁移过程更加平滑,同时为未来的需求提供了更多的灵活性。
2.4 三者比较
以下是 RestTemplate
、WebClient
和 RestClient
的特性对比表:
特性 | RestTemplate | WebClient | RestClient |
---|---|---|---|
同步/异步 | 同步 | 异步 | 同步/异步 |
API 风格 | 模板方法 | 流畅构建者 | 流畅构建者 |
响应式 | 否 | 是 | 部分 |
性能 | 较低 | 较高 | 中等 |
复杂性 | 较低 | 较高 | 中等 |
Spring 版本 | 旧版本 | Spring 5+ | Spring 6.1+ |
2.4.1 重要考虑因素
- 性能: 对于需要处理高并发和非阻塞操作的场景,
WebClient
显然是最佳选择,它提供了高性能和良好的资源利用效率。 - 复杂性:
RestTemplate
提供了简单易用的 API,适合初学者和简单用例;而WebClient
和RestClient
提供了更多高级特性和灵活性,但复杂性也相应较高。 - 项目需求: 如果您的项目需要同步行为和易用性,
RestTemplate
可能已经足够。如果您正在构建现代响应式应用程序,WebClient
是最佳选择。RestClient
则提供了一种在这两者之间的平衡方案,适合那些需要异步操作但不完全响应式的场景。 - Spring 版本: 选择客户端时,请确保与您当前使用的 Spring 版本兼容。
RestTemplate
是较旧版本的选择,而WebClient
和RestClient
则需要较新的 Spring 版本。
2.4.2 建议
- 新项目: 如果您正在开始一个新项目,优先考虑使用
WebClient
,因为它提供了最佳的性能和响应式能力,适合现代应用程序的需求。 - 现有项目: 对于已经使用
RestTemplate
的现有项目,如果计划迁移到更现代的客户端,RestClient
可以作为一个很好的过渡步骤,逐步引入异步和非阻塞特性。 - 简单用例: 对于简单的 HTTP 请求和同步操作,
RestTemplate
仍然是一个合适的选择,特别是在性能和复杂性要求不高的情况下。