WebClient和RestTemplate

本文探讨了Spring框架中的RestTemplate与WebClient在处理HTTP请求时的阻塞与非阻塞模式,通过实例展示了两者在请求响应速度上的区别,以及WebClient如何利用异步任务处理机制提高效率。

RestTemplate是阻塞性客户端,每个事件都为一个线程,客户端在收到服务器响应前会一直被阻塞
WebClient是非阻塞性客户端,每个事件都为一个任务,Spring Reactive框架对任务进行队列处理,仅在收到响应时才执行对应的任务

Java实现

服务

 @GetMapping("/getAll")
    public Person server(String name) throws InterruptedException {
        System.out.println("Enter Server");
        Thread.sleep(2000);
        Person person = new Person(name, 25);
        System.out.println("Server finished");
        return person;
}

进入服务时打印服务开始,睡眠2秒且做一个无意义的操作模拟服务器处理数据,服务返回前打印服务结束

RestTemplate

@GetMapping("/RT")
    public String getRT() {
        System.out.println("Enter RTController+++++++++");
        RestTemplate restTemplate = new RestTemplate();
        Person person = restTemplate.getForObject("http://localhost:8080/getAll?name={name}", Person.class, "skye1");
        System.out.println("RTController Finished+++++++");
        return "true";
}

开始启用方法时打印进入方法,getForObject返回对象,getForEntity返回包含响应体的整个对象,离开方法时打印方法结束

WebClient

    @GetMapping("/WC")
    public String getWC() throws InterruptedException {
        System.out.println("Enter WCController--------");
        WebClient webClient = WebClient.builder()
                .baseUrl("http://localhost:8080")
                .build();
        webClient.get().uri(uriBuilder -> {
            uriBuilder = uriBuilder.path("/getAll");
            uriBuilder = uriBuilder.queryParam("name", "skye2");
            return uriBuilder.build();
        })
                .retrieve()
                .bodyToMono(Person.class).subscribe();
        System.out.println("WC finished---------");
        System.out.println("main thread sleep to wait for WC return");
        Thread.sleep(10000);
        System.out.println("wake up");
        return "true";
}

进入方法时打印进入方法,build webclient,发起get请求,使用subscribe异步获取响应(block则是同步阻塞式获取响应)
结束方法时打印方法结束,为了防止主线程关闭导致服务不执行,主线程睡眠10秒

控制台信息

RestTemplate

Enter RTController+++++++++
Enter Server
Server finished
RTController Finished+++++++

运行时可以明显感觉到停顿了2秒

WebClient

Enter WCController--------
WC finished---------
main thread sleep to wait for WC return
Enter Server
Server finished
wake up

先结束主方法,在主线程睡眠等待时,进入服务,10秒后唤醒

在 Spring Boot 中,`WebClient` 是从 **Spring 5** 开始引入的响应式、非阻塞的 HTTP 客户端,是 `RestTemplate` 的现代替代品。它基于 **Reactor 项目(Project Reactor)**,支持响应式编程模型(Reactive Streams),适用于高并发场景。 特别是在 **Spring Boot 2.0+**(尤其是与 WebFlux 搭配时)中,官方推荐使用 `WebClient` 替代 `RestTemplate`。 --- ### ✅ 1. 添加依赖(Maven) 确保你的 `pom.xml` 包含了 `spring-boot-starter-webflux`: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> ``` > 即使你使用的是传统的 MVC 项目(`spring-boot-starter-web`),也可以添加 `webflux` 来使用 `WebClient`。 --- ### ✅ 2. 配置 WebClient Bean 建议将 `WebClient` 声明为 Spring 管理的 Bean,以便复用集中配置超时、基础 URL 等。 ```java @Configuration public class WebClientConfig { @Bean public WebClient webClient() { return WebClient.builder() .baseUrl("https://jsonplaceholder.typicode.com") // 设置基础 URL .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .clientConnector(new ReactorClientHttpConnector( HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) // 连接超时 .responseTimeout(Duration.ofSeconds(5)) // 响应超时 )) .build(); } } ``` --- ### ✅ 3. 使用 WebClient 发起 GET 请求 #### 示例:获取用户信息(同步阻塞方式 `.block()`) 虽然 `WebClient` 是响应式的,但你可以通过 `.block()` 转为同步调用(适合过渡期或简单场景): ```java @Service public class UserService { @Autowired private WebClient webClient; public String getUserAsString(int id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .bodyToMono(String.class) .block(); // 阻塞等待结果(不推荐在纯响应式服务中使用) } public User getUserAsObject(int id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .bodyToMono(User.class) .block(); // 同步获取对象 } } ``` #### 异步非阻塞方式(推荐): ```java public Mono<User> getUserAsync(int id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .bodyToMono(User.class); } ``` 然后在 Controller 中返回 `Mono` 或 `Flux`,实现完全响应式处理: ```java @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/user/{id}") public Mono<User> getUser(@PathVariable int id) { return userService.getUserAsync(id); // 直接返回响应式类型 } } ``` --- ### ✅ 4. POST 请求示例 ```java public Mono<User> createUser(Map<String, Object> userData) { return webClient.post() .uri("/users") .bodyValue(userData) .retrieve() .bodyToMono(User.class); } ``` 调用示例: ```java Map<String, Object> data = new HashMap<>(); data.put("name", "John Doe"); data.put("email", "john@example.com"); Mono<User> result = userService.createUser(data); User user = result.block(); // 如果需要同步结果 ``` --- ### ✅ 5. 添加请求头 ```java public Mono<User> getUserWithAuth(int id) { return webClient.get() .uri("/users/{id}", id) .header("Authorization", "Bearer fake-jwt-token") .header("X-Custom-Header", "MyApp") .retrieve() .bodyToMono(User.class) .onErrorMap(WebClientResponseException.class, ex -> { System.err.println("Error status: " + ex.getStatusCode()); return new RuntimeException("Failed to fetch user", ex); }); } ``` --- ### ✅ 6. 错误处理 `.retrieve()` 方式下可以使用 `.onStatus()` 自定义错误逻辑: ```java public Mono<User> getUserWithErrorHandler(int id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new RuntimeException("Client error: " + response.statusCode())) ) .onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new RuntimeException("Server error: " + response.statusCode())) ) .bodyToMono(User.class); } ``` 或者使用 `.exchangeToMono()` 获取完整控制权(更灵活): ```java public Mono<String> getUserWithExchange(int id) { return webClient.get() .uri("/users/{id}", id) .exchangeToMono(response -> { if (response.statusCode().equals(HttpStatus.OK)) { return response.bodyToMono(String.class); } else if (response.statusCode().is4xxClientError()) { return Mono.just("Client error occurred"); } else { return Mono.just("Server error occurred"); } }); } ``` --- ### ✅ 7. 设置 Cookie、Basic Auth 等 #### Basic 认证: ```java webClient.get() .uri("/secure-data") .headers(h -> h.setBasicAuth("username", "password")) .retrieve() .bodyToMono(String.class) .block(); ``` #### 添加 Cookie: ```java webClient.get() .uri("/profile") .cookie("JSESSIONID", "123456") .retrieve() .bodyToMono(String.class) .block(); ``` --- ### ✅ 8. 对比 RestTemplate WebClient | 特性 | `RestTemplate` | `WebClient` | |------|------------------|-------------| | 类型 | 同步、阻塞 | 异步、非阻塞、响应式 | | 所属模块 | `spring-web` | `spring-webflux` | | 是否推荐新项目使用 | ❌ 已进入维护模式 | ✅ 官方推荐 | | 支持函数式编程 | 否 | 是(支持 `Mono`/`Flux`) | | 性能 | 低并发下尚可 | 高并发下表现更好 | | 学习成本 | 低 | 中等(需了解 Reactor) | --- ### ✅ 总结:迁移建议 - 新项目优先选择 `WebClient` - 老项目逐步替换 `RestTemplate` → `WebClient` - 若无法立即切换到响应式栈(如用了 `@RestController` + MVC),仍可用 `WebClient` + `.block()` 作为过渡 - 最终目标是使用全响应式栈(`WebFlux` + `Reactive Repositories`) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

skye111321

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值