Spring 中 HTTP 客户端的现状

工程 | Brian Clozel | 2025年9月30日 | ...

这是 Road to GA 系列 中的一篇新博客文章,这次探讨了我们 HTTP 客户端的新功能。这也是反思 Spring 中 HTTP 客户端状态的好时机,所以我们将借此机会解释一个重要公告:我们正式弃用 RestTemplate

即将推出的 RestClient 功能

RestClient 已在 Spring Framework 6.1 中引入,并在 6.x 系列中不断发展。在即将发布的 7.0 主要版本中,我们将通过一系列新功能保持这种发展速度。

API 版本控制

Spring @Controller 现在支持 API 版本控制概念,以在单个应用程序中更好地实现不同代的 REST API。此功能也受客户端支持,通过使用 ApiVersionInserter。例如,您可以在自定义 HTTP 请求头中插入 API 版本信息。这可以在构建客户端时进行配置。

RestClient customClient = RestClient.builder()
  .baseUrl("https://springframework.org.cn/api")
  .defaultVersion("1.2")
  .apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
  .build();

Spring 支持从 HTTP 头、媒体类型、请求路径、查询参数或任何自定义实现中插入和解析 API 版本。

HttpMessageConverters

我们重新审视了 Spring 应用程序中消息转换器的配置方式。HttpMessageConverters 带来了一个专用的 API,大大简化了替换现有转换器或添加自定义转换器的自定义安排。在这里,我们将从类路径中自动检测消息转换器,并仅使用自定义 Jackson JsonMapper 覆盖 JSON 支持的转换器。

JsonMapper jsonMapper = JsonMapper.builder()
  .findAndAddModules()
  .enable(SerializationFeature.INDENT_OUTPUT)
  .defaultDateFormat(new SimpleDateFormat("yyyy-MM-dd"))
  .build();

RestClient restClient = RestClient.builder()
  .configureMessageConverters(client -> {
    client.registerDefaults().jsonMessageConverter(new JacksonJsonHttpMessageConverter(jsonMapper));
  })
  .baseUrl("https://springframework.org.cn/api")
  .build();

Spring Boot 之前通过其自己的 HttpMessageConverters 类型来满足这一需求;新的 Framework 变体旨在在 Spring Boot 应用程序中取代它。在 WebFlux 方面,Spring Framework 已经为编解码器提供了响应式等效项 CodecConfigurer

Http 接口组

当应用程序需要配置许多 Http 接口客户端时,它们的设置可能会非常重复且难以组织。这个新版本引入了组的概念:一种强大的方式,可以一次声明和配置许多客户端,并在有意义时让它们共享相同的 RestClient

在这里,我们定义了一个“stackexchange”组,用于查询 StackOverflow 和 ServerFault API。另一个组“github”将为同一基本包中定义的所有接口共享相同的 HTTP 客户端。

@Configuration
@ImportHttpServices(group = "stackexchange", types = {StackOverflowClient.class, ServerFaultClient.class})
@ImportHttpServices(group = "github", basePackageClasses = GitHubProjects.class)
public class ClientConfig {

}

您可以在 “HTTP 服务客户端增强”博客文章中了解有关此功能的更多信息。

RestTestClient

Spring Framework 7.0 将发布 RestTestClient,这是一个用于测试服务器应用程序的新客户端。RestTestClient 可以对实时服务器执行集成测试,这意味着它们会测试整个网络堆栈和消息转换。RestTestClient 还可以像 MockMvc 一样,使用模拟请求和响应测试单个控制器或路由器函数。实时服务器测试和基于模拟的测试都使用相同的 API 来执行交换和断言。

这是社区的一个热门请求,因为 WebTestClient 在响应式堆栈中填补了这一空白,但没有 RestClient 变体。

这个新功能比 Spring Boot 的 TestRestTemplate 带来了更多价值,可以被视为替代品,这就是 团队正在考虑弃用它的原因。

新的 Spring Boot Starter

社区多次提出关于 HTTP 客户端在 Spring 工件中打包方式的担忧。RestClientRestTemplate 位于“org.springframework:spring-web”中,而 WebClient 位于“org.springframework:spring-webflux”中。这些都没有单独的专用客户端工件。

这种打包结构使得 Spring Boot 更难理解开发人员的意图。应用程序应该启动 Web 服务器、自动配置 HTTP 客户端,还是两者兼而有之?这个问题现在通过即将发布的 Spring Boot 版本中的一个主要功能得到解决:整个 Spring Boot 代码库已经模块化,并且自动配置被拆分为单独的工件

借助 Spring Boot 4.0,应用程序现在可以使用“org.springframework.boot:spring-boot-starter-webclient”或“org.springframework.boot:spring-boot-starter-restclient”来表达对 HTTP 客户端的需求。

更多!

我们还进行了许多其他较小的改进,例如内置缓冲支持客户端 Jackson 提示支持或Http 接口客户端的 Spring Security 支持

RestTemplate 的局限性

如果您的应用程序正在使用 RestTemplate,您很快就会注意到上面提到的大多数新功能不适用于 RestTemplate。在本节中,我们将解释为什么我们无法为 RestTemplate 实现这些功能。

RestTemplate 是在 15 多年前的 Spring Framework 3.0 中发布的,自那以后 Java 生态系统发生了许多变化。

多年来,我们开发 RestTemplate 和所有其他 Spring HTTP 客户端的动机始终如一:为社区提供一个与 Spring 库良好集成的高级 HTTP 客户端。消息体转换、错误处理、拦截机制和安全性都是您可以使用熟悉的 Spring 编程模型使用的功能。我们支持许多 HTTP 客户端库,但我们不暴露低级 HTTP 交互或特定配置:您可以选择适合您需求的那个,但这不应改变整体开发体验。

这种方法多年来对 RestTemplate 来说运作良好,但我们发现这种模型存在一些局限性。

“模板式”API 在有限范围内工作良好,但在添加新功能时可能会面临挑战。这是 Spring Framework 中的一个常见模式,始于 JdbcTemplateJmsTemplate,它们早于 HTTP 客户端。使用这种模式,当范围扩大时,方法命名空间会很快变得拥挤,并且使用方法重载来实现变体增加了问题。RestTemplate 的 Javadoc 显示了许多重载方法,IDE 自动补全也一样。任何时候引入新功能,开发人员体验都可能会受到影响。例如,现在在 RestTemplate 中引入 API 版本控制支持将需要新的构造函数并增加 Java API 的混乱。因此,我们无法为 RestTemplate 提供此功能。

使用 AsyncRestTemplate 变体支持异步调用,但这种方法也存在一些问题。由于“模板式”API,方法重载在那里是一个更大的问题。Spring 的 ListenableFuture 在当时是一个必要的选择,但我们受限于其组合并发调用和高效管理底层 HTTP 资源的能力。当然,如今 Java 平台提供了更好的替代方案。

流式 HTTP 协议,如“服务器发送事件”,也越来越受欢迎。对于 RestTemplate 来说,这种用例更具挑战性,因为典型的方法是一次性接收和转换整个响应,然后关闭它;而流式处理需要保持响应在整个流期间处于活动状态,并逐项执行消息转换。

虽然 RestTemplate 存在局限性,但在 Spring 社区中非常受欢迎。不幸的是,模板式 API 达到了其极限,需要彻底改革。Spring 团队有机会在 Spring Framework 5 中解决这个问题,并推出了一个名为 WebClient 的新 HTTP 客户端。

WebClient 和响应式空间

在维护 Spring Framework 4.x 时,我们听到了 Java 和 Spring 社区中很大一部分人的呼声:对支持保持低延迟的异步、非阻塞 Web 堆栈的需求日益增长。新的 Web 框架出现了,其中许多使用函数式编程来组合异步操作。响应式流成为了行业标准,RSocket 等协议也应运而生。尽管存在学习曲线,许多团队选择这种方法来克服传统 Java 异步原语和线程池的局限性。

在 Spring Framework 5.0 中,Spring 团队引入了一个基于 Reactive Streams 和 Reactor 的完整响应式堆栈。从一开始,我们就明确表示传统的 Servlet 堆栈不会消失,并且团队只有在面临严重的运行时挑战时才应考虑这种方法。管理延迟和组合异步操作是关键,而最佳性能从来都不是主要目标。

在 HTTP 客户端方面,这对团队来说是一个机会,可以重新审视之前的决策并应用我们从过去中学到的经验。

我们没有选择“模板式”API,而是为我们的新 HTTP 客户端 WebClient 选择了一种流式 API。这是新系列流式客户端中的第一个,之后是 JdbcClientJmsClient。流式 API 显著减少了任何给定类型的方法数量。IDE 中的自动补全体验更加集中,因为流式链中的每个方法只反映了该点的可用选项,而不是一次性呈现所有可能的选项。

异步、非阻塞基础设施是 Reactor 的内置功能。流数据也是响应式流中的一个自然概念。但是流式请求和响应改变了消息转换层面的动态。我们不再将数据流作为一个整体来考虑,而是需要将数据解析和写入为字节缓冲区。这还与 DataBuffer 契约(而不是 byte[])相结合,可以池化和重用缓冲区以提高内存效率。

然后 WebClient 作为一个很好的 RestTemplate 替代品出现了。扩展点和 API 风格都很现代。如果高并发或流式 HTTP 调用是应用程序的重要功能,WebClient 是一个可行的解决方案。我们不期望仅仅因为这个原因就将整个应用程序用 WebFlux 重写。这就是为什么我们确保 Spring MVC 控制器会自然处理响应式类型作为传统的异步调用。当然,如果需要,应用程序也可以调用 block() 返回到同步世界。

那时,我们宣布 RestTemplate 为“功能完整”:由于其局限性,我们无法在那里承诺新功能。鉴于 WebClient 在此类需求方面的优越性,我们当时弃用了 AsyncRestTemplate:其异步和非阻塞支持,以及组合操作和流式管道的能力远超其前。总的来说,WebClient 是一个更好的全能替代品,当时我们希望社区最终能收敛于一个单一的解决方案。

引入 RestClient

随着 Spring Framework 6.0 中新的 Java 17 基线和 Java 21 中计划支持虚拟线程,我们看到了重新审视 RestTemplate 的机会。我们利用从 WebClient 中学到的经验,通过现代、流式 API 解决了“模板式”的局限性。我们选择重用 RestTemplate 现有的 HTTP 基础设施,并将这个新客户端放在同一个包中。这使得迁移更加容易,因为您可以使用现有的 RestTemplate 创建 RestClient 实例。我们还发布了专门的迁移指南,以帮助开发人员完成该过程。

回想起来,选择保持命令式、阻塞式 API 是一个很好的选择:结构化并发RestClient 解锁了异步组合,而无需额外的更改。我们仍然不确定流式用例(稍后会详细介绍)。

在最近几个版本中,我们看到了社区对 RestClient 的快速采用,但直到现在,我们还没有重新审视我们对 RestTemplate 的维护立场。

Spring 中 HTTP 客户端的未来

到目前为止,RestTemplateRestClient 之间存在显着差距,由于模板式 API 的限制,我们无法弥合。保留 RestTemplate 会使我们社区的一部分处于劣势。我们现在认为是时候重新考虑我们的 HTTP 客户端安排了,这是我们当前的计划。

  1. Spring Framework 7.0(2025 年 11 月):在此博客文章、我们的升级指南和参考文档中宣布我们弃用 RestTemplate 的意图。
  2. Spring Framework 7.1(暂定日期,2026 年 11 月):正式“@Deprecate”该客户端并标记为移除。
  3. Spring Framework 8.0(日期待定):完全移除 RestTemplate

假设我们目前的发布速度,RestTemplate 的开源支持将至少持续到 2029 年。我们坚信,更新应用程序以使用 RestClient(和相同的底层基础设施)是最佳的长期解决方案。如果您的团队维护自定义请求工厂或客户端拦截器,这些都可以与 RestClient 重用。应用程序可以迭代升级,最初将现有的 RestTemplate 实例包装在 RestClient 中,然后重构共享配置。

这种新安排使我们拥有用于传统堆栈的 RestClient 和用于响应式变体的 WebClient,两者都由团队积极维护。决策过程变得容易得多:在大多数情况下,您可以选择 RestClient,或者如果您需要响应式 API 或流功能,则选择 WebClient

现在,回到流式用例。我们仍在探索在 RestClient 中实现适当流式支持的选项。目前 Java 平台中没有明确的流式支持路径,也没有一个能自然地与虚拟线程和结构化并发配合。我们正在与 Java 平台团队和更广泛的行业保持联系。

我们即将迎来 Spring Framework 7.0 和 Spring Boot 4.0 的发布日期。您能否用您的一个应用程序试用最新的里程碑版本?现在仍然是改进升级体验并让您在不久的将来工作更轻松的时候。一如既往,您的意见很重要——请不要犹豫,在此处评论中分享您使用 Spring HTTP 客户端的经验!

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有