从RestTemplate到RestClient:Spring HTTP客户端的现代化演进

news2026/3/18 0:56:48
1. 老朋友RestTemplate曾经的功臣与如今的困境如果你用Spring做过项目特别是几年前的项目大概率会碰到RestTemplate。它就像是Spring生态里一个任劳任怨的老伙计帮你处理各种HTTP请求调用外部API简单直接。我刚开始用的时候也觉得挺方便getForObject、postForEntity方法名一看就懂配个RestTemplate的Bean就能开干。在单体应用或者并发不高的场景下它确实够用也陪伴了无数项目从零到一。但技术这玩意儿发展得太快了。随着微服务架构成为主流一个服务动不动就要调用几十个其他服务的接口这时候RestTemplate的一些“老毛病”就开始暴露出来了。最核心的问题就是它的阻塞式同步模型。什么叫阻塞式简单说就是当你用RestTemplate发起一个请求时执行这行代码的线程会一直“傻等”在那里直到收到远端的响应或者超时。想象一下你的服务线程池里一共就50个线程如果同时有50个请求都在调用一个响应慢的外部服务那么这50个线程就全被“卡住”了。这时候再来第51个请求对不起没线程可用了只能排队或者直接失败。这就是在高并发下可能导致线程池耗尽的典型场景系统的吞吐量一下子就到了天花板。另一个让我头疼的问题是它的API设计。RestTemplate是在Spring 3时代诞生的那时候的设计理念和现在不太一样。它提供了海量的重载方法光是一个getForObject就有好几种变体。刚接触时你可能觉得“哇功能真全”但用久了维护起来就痛苦了。代码里到处是restTemplate.exchange(uri, HttpMethod.GET, requestEntity, responseType)这种冗长的调用可读性一般。而且它完全跟不上反应式编程的浪潮。现在很多新项目都在用WebFlux做响应式架构追求更高的资源利用率和更低的延迟但RestTemplate是同步阻塞的跟反应式那套非阻塞、背压的模型格格不入你想把它集成到反应式调用链里会非常别扭。所以尽管RestTemplate功不可没但Spring官方在文档里也明确表示它已经进入了维护模式不再推荐在新的项目中使用。这就像是你家里有一台老式的显像管电视机还能看但体积大、耗电高、功能少是时候考虑换一台更轻薄、更智能的液晶电视了。2. 新星RestClient为何它是现代化的选择那么谁来接RestTemplate的班呢答案就是RestClient。从Spring Framework 6.1和Spring Boot 3.2开始它正式登场目标就是成为一个更现代、更友好、能力更强的HTTP客户端。我把它看作是RestTemplate的“精神续作”继承了其同步调用的直观性但内核和API设计都全面升级了。首先最直观的感受就是API变得无比流畅。RestClient采用了建造者Builder模式和流式FluentAPI设计写出来的代码读起来就像是在说英语句子。比如你想发起一个GET请求代码会是restClient.get().uri(/users).retrieve().body(User.class)一气呵成意图非常清晰。这种设计大大减少了样板代码也降低了记忆负担你不再需要去翻文档查某个重载方法到底该传哪些参数了。其次RestClient在可测试性上做了精心设计。做过单元测试的朋友都知道模拟HTTP调用是个麻烦事。RestClient通过MockRestServiceServer可以非常方便地进行测试。你可以在测试中精确地模拟服务端返回什么状态码、什么响应体然后验证你的客户端代码逻辑是否正确。这种“开箱即用”的测试支持对于保证代码质量至关重要也是现代框架必备的素质。还有一个杀手级特性就是对服务发现和负载均衡的原生支持。如果你在用Spring Cloud那么从某个版本开始RestClient可以直接和负载均衡器如Spring Cloud LoadBalancer集成。这意味着你不再需要手动拼接服务实例的URL而是直接使用服务名比如http://user-serviceRestClient会自动帮你从注册中心发现可用的实例并做负载均衡。这对于构建微服务来说简直是如虎添翼简化了配置也提升了系统的弹性。最后对比另一个现代化客户端WebClientRestClient还有一个优势轻量无需额外依赖。WebClient是反应式非阻塞的功能强大但它需要引入spring-boot-starter-webflux这个依赖。如果你的项目本身不是反应式架构引入它可能有点“杀鸡用牛刀”。而RestClient就简单多了它在Spring Boot的Web模块spring-boot-starter-web中就直接提供了开箱即用没有额外的学习成本和依赖负担。3. 深入对比设计理念与性能差异光说优点可能有点抽象我们不妨把RestTemplate和RestClient拉出来从几个关键维度做个实实在在的对比这样你就能更清楚为什么后者是更好的选择。设计哲学对比RestTemplate的设计哲学是“功能完备的工具箱”。它把各种可能的HTTP操作GET、POST、PUT等和参数组合都封装成了一个个独立的方法。这就像给你一个装满各种型号螺丝刀和扳手的工具箱你需要什么就找什么。好处是直接缺点是工具太多容易挑花眼而且工具箱本身比较笨重API臃肿。RestClient的设计哲学则是“流畅的表达链”。它提供了一个统一的入口然后通过链式调用来组装你的请求。这就像乐高积木给你一些基础模块方法你可以按照自己的需求自由组合出想要的形态。这种方式更加灵活、表达力更强也符合现代API设计的潮流。性能考量虽然两者默认都是同步阻塞的但它们的底层实现和扩展性不同这间接影响了性能表现。RestTemplate默认使用JDK的HttpURLConnection或者Apache的HttpClient作为底层引擎。它的阻塞是“硬阻塞”线程在等待IO时完全被占用。在高并发下如前所述容易导致线程资源紧张。RestClient它是一个更高层次的抽象底层可以灵活适配不同的HTTP客户端库比如JDK 11自带的HttpClient、ApacheHttpComponents等。更重要的是这种设计为未来的优化留下了空间。虽然当前主要用作同步客户端但其底层基础设施与反应式栈是相通的未来如果需要向非阻塞模式迁移或集成会更平滑。为了更直观我们看一个简单的对比表格特性维度RestTemplateRestClient说明API风格传统、基于重载方法现代、流式FluentRestClient代码更简洁意图更清晰。阻塞模型同步阻塞同步阻塞当前两者当前都是同步的但RestClient底层更灵活。反应式支持不支持底层基础设施支持API暂为同步RestClient未来更容易与反应式编程集成。测试便利性支持但稍显繁琐优秀原生支持MockRestServiceServerRestClient的测试支持更友好、更强大。服务发现集成需通过LoadBalanced注解原生支持需Spring Cloud环境RestClient与Spring Cloud生态集成更紧密、更现代。依赖需求spring-boot-starter-webspring-boot-starter-web两者基础依赖相同RestClient无需额外引入反应式依赖。官方推荐度维护模式不推荐新项目使用推荐用于新的同步HTTP调用Spring官方明确建议新项目使用RestClient。从表格可以看出RestClient几乎在每一个方面都做了优化和增强特别是在API设计、可测试性和与现代云原生生态的集成上优势明显。4. 实战上手从零开始使用RestClient说了这么多不如动手写两行代码来得实在。下面我就带你一步步把RestClient用起来你会发现迁移成本其实非常低。第一步引入依赖如果你用的是Spring Boot 3.2或更高版本那么恭喜你RestClient已经包含在spring-boot-starter-web里了你什么都不用加。如果是旧项目升级确保你的Spring Boot版本至少是3.2并引入了web starter即可。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency第二步创建RestClient实例和RestTemplate一样我们通常把它配置成一个Spring Bean。有两种常见方式简单创建如果你只需要一个基础的客户端用create()方法最快。Configuration public class RestClientConfig { Bean public RestClient restClient() { // 可以指定一个基础URL后续请求的uri会相对这个路径 return RestClient.create(https://api.example.com); } }自定义构建大部分时候我们可能需要设置连接超时、读写超时或者使用特定的HTTP客户端库如Apache HttpClient。这时就用builder()方法。Bean public RestClient customRestClient(RestTemplateBuilder builder) { // 利用Spring Boot自动配置的RestTemplateBuilder来构建它能自动应用配置文件中的属性 return builder .baseUrl(https://api.example.com) .defaultHeader(User-Agent, MyApp/1.0) .requestInterceptor((request, body, execution) - { // 可以添加统一的请求拦截器比如打印日志、添加认证头 System.out.println(Sending request to: request.getURI()); return execution.execute(request, body); }) .build(); }你也可以直接在application.yml中配置超时等属性Spring Boot会自动应用到通过RestTemplateBuilder创建的客户端上spring: rest: client: timeout: connect: 2s read: 5s第三步发起HTTP请求这是最核心的部分我们看看几种常见操作。GET请求 - 获取资源Service public class UserService { private final RestClient restClient; public UserService(RestClient restClient) { this.restClient restClient; } public User getUserById(Long id) { // 链式调用非常清晰获取 - 设置URI - 执行并获取响应 - 提取响应体 User user restClient.get() .uri(/users/{id}, id) // 支持URI模板变量 .header(Authorization, Bearer my-token) // 设置请求头 .accept(MediaType.APPLICATION_JSON) // 设置Accept头 .retrieve() // 执行请求并获取响应 .body(User.class); // 将响应体反序列化为Java对象 return user; } public ListUser getAllUsers() { // 响应体是数组或列表的情况 ListUser users restClient.get() .uri(/users) .retrieve() .body(new ParameterizedTypeReferenceListUser() {}); return users; } }retrieve()方法表示你只关心成功的响应体。如果服务端返回4xx或5xx错误它会抛出RestClientException的子类如HttpClientErrorException、HttpServerErrorException你可以通过ExceptionHandler或try-catch来处理。POST请求 - 创建资源public User createUser(User newUser) { User createdUser restClient.post() .uri(/users) .contentType(MediaType.APPLICATION_JSON) // 设置Content-Type .body(newUser) // 设置请求体会自动序列化为JSON .retrieve() .body(User.class); return createdUser; } // 如果不关心响应体只关心状态码 public void createUserWithoutResponse(User newUser) { restClient.post() .uri(/users) .contentType(MediaType.APPLICATION_JSON) .body(newUser) .retrieve() .toBodilessEntity(); // 适用于POST/PUT/DELETE等操作忽略响应体 }处理错误响应 有时候你可能想更精细地处理非2xx的响应而不是直接抛出异常。可以用onStatus方法。public User getUserSafe(Long id) { return restClient.get() .uri(/users/{id}, id) .retrieve() .onStatus(status - status.value() 404, (request, response) - { // 当状态码为404时抛出自定义异常 throw new UserNotFoundException(User with id id not found); }) .onStatus(status - status.value() 500, (request, response) - { // 当状态码为5xx时记录日志或进行其他处理 log.error(Server error while fetching user); }) .body(User.class); }第四步编写单元测试测试是RestClient的一大亮点。Spring提供了MockRestServiceServer来模拟后端服务。SpringBootTest public class UserServiceTest { Autowired private UserService userService; Autowired private RestClient restClient; // 注入被测试的RestClient private MockRestServiceServer mockServer; BeforeEach void setUp() { mockServer MockRestServiceServer.bindTo(restClient).build(); } Test void testGetUserById() { // 1. 定义当请求匹配某个条件时返回什么响应 mockServer.expect(requestTo(https://api.example.com/users/1)) .andRespond(withSuccess({\id\:1,\name\:\张三\}, MediaType.APPLICATION_JSON)); // 2. 执行被测试的方法 User user userService.getUserById(1L); // 3. 验证结果 assertThat(user.getId()).isEqualTo(1L); assertThat(user.getName()).isEqualTo(张三); // 4. 验证所有预期的请求都已被执行 mockServer.verify(); } }通过这种方式你可以完全隔离外部依赖快速、可靠地测试你的客户端逻辑。5. 迁移指南与最佳实践如果你手头有正在使用RestTemplate的老项目想要迁移到RestClient别担心这个过程通常是渐进且平滑的。我结合自己的迁移经验给你几点建议。渐进式迁移而非一刀切 不要试图在一个PR里替换掉项目中所有的RestTemplate。最好的方法是“新人新办法老人老办法”。对于新开发的模块或接口直接使用RestClient。对于已有的、稳定的代码可以先不动等到需要修改或重构时再顺便将其替换为RestClient。这样风险可控也不会给团队带来过大的负担。利用IDE的查找替换和包装模式RestTemplate的调用通常模式固定。你可以利用IDE的“查找引用”功能找到所有使用RestTemplate的地方。对于简单的getForObject、postForEntity调用可以手动或写个小脚本将其转换成RestClient的流式API。对于更复杂或分散的调用可以考虑先创建一个RestClient的包装类这个类内部使用RestClient但对外提供与原有RestTemplate类似的方法签名这样调用方的代码几乎不用改动只需替换注入的Bean类型即可。配置的统一管理 之前RestTemplate的超时、拦截器等配置可能散落在各个Bean定义方法里。迁移到RestClient时建议利用Spring Boot的自动配置特性将通用配置如基础URL、默认超时、公共请求头集中到application.yml或一个统一的配置类中。通过RestTemplateBuilder或自定义的RestClientBuilder来创建Bean能让配置更加清晰和可维护。关注异常处理的差异RestTemplate和RestClient在错误处理上略有不同。RestTemplate的一些方法如getForObject在遇到4xx/5xx时可能返回null或抛出异常行为取决于具体方法。而RestClient的retrieve()方法在遇到非2xx状态码时会统一抛出异常。这意味着你的全局异常处理器ControllerAdvice或现有的try-catch块可能需要调整以捕获HttpClientErrorException或HttpServerErrorException。充分利用新特性 迁移不仅仅是API的简单替换更是享受新特性红利的机会。比如尝试流式API用RestClient的链式调用重写旧代码你会发现代码行数减少了可读性提高了。引入更优雅的测试为新写的RestClient代码配套编写基于MockRestServiceServer的单元测试你会发现测试代码更简洁意图更明确。探索与Cloud集成如果你的项目是微服务架构并使用了Spring Cloud尝试配置RestClient与负载均衡器的集成体验直接使用服务名进行调用的便捷。从我实际迁移的几个项目来看虽然初期需要一些学习和适配但一旦熟悉了RestClient的套路开发效率和对代码的控制力都有明显的提升。特别是当项目需要与多个外部API交互时RestClient清晰的API和强大的自定义能力让代码维护起来轻松了不少。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2421207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…