在Spring中过去的十多年一直用 RestTemplate,从内部项目、微服务到数据中台,它几乎是我写 Java 调用 HTTP 时的“肌肉记忆”。
但在一次升级 Spring Boot 3.x 后,我彻底改用 WebClient ——并且永不回头。
它不是“新玩具”,也不是“更潮的写法”。
它是一个整个调用链更优雅、性能更强、可扩展性更高的解决方案。
今天,我讲清楚四个问题:
- 为什么 RestTemplate 被官方判死刑?
- WebClient 到底好在哪里?
- 用 WebClient 真有那么爽?(实战场景)
- 迁移方案:从 RestTemplate → WebClient(含代码)
1. 为什么 RestTemplate 被判“已废弃”?
先给你看 Spring 官方态度:
RestTemplate 已进入维护模式(Deprecated),不再新增功能。
理由非常简单:
❌ 1. RestTemplate 是阻塞式的(Blocking I/O)
我们使用的是RestTemplate内部同步端点:
@GetMapping("/users/{id}")public ResponseEntity<User> getUser(@PathVariable String id) { User user = restTemplate.getForObject( "http://external-service/users/" + id, User.class ); return ResponseEntity.ok(user);}每个请求都会创建一个 servlet 线程。该线程会一直处于空闲状态,等待 HTTP 响应。当并发请求数达到每分钟数千个时,线程就耗尽了
一次请求会死死占着一个线程。
- JVM 线程很贵
- 大量并发时会直接炸线程池
- 在微服务时代这是灾难性设计
解决方案:切换到 WebClient + WebFlux
我们采用了非阻塞、响应式WebClient模型
@RestControllerpublic class UserController { private final WebClient webClient = WebClient.create("http://*-service"); @GetMapping("/users/{id}") public Mono<ResponseEntity<User>> getUser(@PathVariable String id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .bodyToMono(User.class) .map(ResponseEntity::ok); }}现在,线程不再处于等待状态。请求通过响应式管道流动,由 Netty 的事件循环处理——没有阻塞。
❌ 2. RestTemplate 扩展性差
想加拦截器、超时配置、限流等,非常麻烦。
❌ 3. RestTemplate 不支持流式数据
例如 SSE、WebSocket、长连接,这些它都玩不转。
❌ 4. RestTemplate 天生不适配响应式架构
Spring WebFlux 需要非阻塞模型,而 RestTemplate 完全配合不上。
所以,Spring 官方推荐:
新项目全部使用 WebClient。老项目逐步迁移。
2. WebClient 到底强在哪里?(核心差异一图看懂)
图片说明:
能力点 | RestTemplate | WebClient |
I/O 模型 | 阻塞(Blocking) | 非阻塞(Reactive) |
并发能力 | 低 / 依赖线程池 | 极高(事件驱动) |
资源占用 | 高(一个请求一线程) | 极低(单线程可跑很多) |
超时/重试 | 麻烦、易踩坑 | 配置灵活,链式优雅 |
文件/流处理 | 弱 | 原生强支持 |
WebFlux 兼容性 | 无 | 天生适配 |
官方维护状态 | 仅维护,不再更新 | 全量功能持续升级 |
一句话概括:
WebClient = 更现代的 RestTemplate + 非阻塞性能 + 完整拦截链能力。
3. WebClient 的设计理念(附翻译后的图片结构)
原文中的图我给你重新整理成了中文版流程示意
✔ WebClient 的请求生命周期
WebClient.create() │ ▼────────────────────────────────────────────【阶段 1:准备请求】- 构建 RequestSpec- 设置 URI / Header / Body──────────────────────────────────────────── │ ▼────────────────────────────────────────────【阶段 2:过滤器链 FilterChain】- 日志拦截器- token 鉴权- error handler- retry / 超时──────────────────────────────────────────── │ ▼────────────────────────────────────────────【阶段 3:底层 HTTP 连接器】- Reactor Netty(默认)- 执行非阻塞 I/O──────────────────────────────────────────── │ ▼────────────────────────────────────────────【阶段 4:Reactive Stream 处理】- Mono 或 Flux- 异步解析响应- 异常流 error signal────────────────────────────────────────────相比 RestTemplate,WebClient 完全是模块化设计,非常适合扩展。
✔ 那些重要的数字
| 测试项 | Before (`RestTemplate`) | After (`WebClient`) || ----------------------- | ----------------------- | ------------------- || Avg Response Time | 480ms | 220ms || 99th Percentile Latency | 1050ms | 430ms || Max Concurrent Requests | ~300 | ~1500 || CPU Usage | ~65% | ~40% |我们实现了性能翻倍,同时降低了基础设施成本。
4. WebClient 实战体验:为什么它“爽到上头”?
下面我列你三个真实场景,你会立刻懂 WebClient 为啥无敌。
场景 1:大量并发请求
RestTemplate:
RestTemplate rest = new RestTemplate();String body = rest.getForObject(url, String.class);高并发时:
- 线程池爆/CPU 飙/吞吐量低/你会被运维挂在嘴边
WebClient:
WebClient client = WebClient.create();Mono<String> body = client.get().uri(url).retrieve().bodyToMono(String.class);非阻塞:
➡ 可用一个线程跑几万并发
➡ 服务器从 4 核 16G 直接降低到 2 核都够
性能提升是质变,不是量变。
场景 2:文件上传/下载(RestTemplate 很折磨)
WebClient:
client.post() .uri(url) .contentType(MediaType.MULTIPART_FORM_DATA) .body(BodyInserters.fromMultipartData("file", resource)) .retrieve() .bodyToMono(String.class)流式处理不占内存
下载大文件不 OOM
场景 3:API 失败重试 + 限流(链式一行解决)
RestTemplate:
要写拦截器 → retryTemplate → 异常处理 → 很恶心。
WebClient:
client.get() .uri(url) .retrieve() .bodyToMono(String.class) .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(1)));是不是瞬间优雅?
5. WebClient 配置比 RestTemplate 美丽一百倍
RestTemplate 想设置超时这样写:
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();factory.setConnectTimeout(1000);factory.setReadTimeout(1000);配置散落 everywhere,很难统一。
WebClient:
@Beanpublic WebClient webClient() { HttpClient httpClient = HttpClient.create() .responseTimeout(Duration.ofSeconds(3)); return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .filter(logFilter()) // 请求日志 .filter(authFilter()) // 鉴权 .build();}你想加什么都能链式加,不会污染逻辑。
6. 迁移指南:如何从 RestTemplate → WebClient?
1) GET 替换
RestTemplate:
restTemplate.getForObject(url, User.class);WebClient:
client.get().uri(url) .retrieve() .bodyToMono(User.class);2) POST 替换
RestTemplate:

restTemplate.postForEntity(url, body, User.class);WebClient:
client.post() .uri(url) .bodyValue(body) .retrieve() .bodyToMono(User.class);3) 同步调用也可以(阻塞)
如果你不想响应式:
User user = client.get() .uri(url) .retrieve() .bodyToMono(User.class) .block(); // 注意:阻塞迁移成本极低。
7. WebClient 不是“更潮”,而是 Java 现代化的标志
RestTemplate 是过去的产物:
- 单体应用时代
- 阻塞模型
- 线程资源不紧缺
WebClient 代表未来:
- 非阻塞
- 高并发
- 云原生命令式/响应式统一
如果你正在做新项目,快来试试WebClient 。在SpringBoot4中就更强了,我们后面再介绍
如果你是 Java / 后端 / 全栈开发者,这篇一定对你有用
本号持续输出:
- Java 核心 & 高并发 & JVM
- Spring / 架构设计 / 实战踩坑
- AI × 编程 / Agent × 工程化
关注我,少踩坑,多走捷径。