从 RestTemplate 迁移到 WebClient
RestTemplate 已在 Spring 5 中被标记为弃用,Spring 官方推荐使用 WebClient 作为替代方案。WebClient 是响应式非阻塞 HTTP 客户端,支持同步和异步调用。
创建 WebClient 实例:
WebClient webClient = WebClient.builder() .baseUrl("https://api.example.com") .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build();执行 GET 请求:
Mono<String> response = webClient.get() .uri("/users/{id}", userId) .retrieve() .bodyToMono(String.class);处理 POST 请求:
Mono<User> createdUser = webClient.post() .uri("/users") .bodyValue(newUser) .retrieve() .bodyToMono(User.class);使用 HTTP Interface 声明式客户端
Spring 6 引入了 HTTP Interface,允许通过接口声明的方式定义 HTTP 客户端。这种方式更加简洁,减少了样板代码。
定义接口:
@HttpExchange(url = "/users", contentType = "application/json") public interface UserClient { @GetExchange("/{id}") User getUser(@PathVariable String id); @PostExchange User createUser(@RequestBody User user); }创建代理实例:
UserClient userClient = WebClient.create() .mutate() .baseUrl("https://api.example.com") .build() .createClient(UserClient.class);处理错误和重试机制
WebClient 提供了灵活的错误处理方式。可以通过 onStatus 方法处理特定状态码:
Mono<User> user = webClient.get() .uri("/users/{id}", userId) .retrieve() .onStatus(status -> status.is4xxClientError(), response -> Mono.error(new UserNotFoundException())) .bodyToMono(User.class);添加重试逻辑:
Mono<User> userWithRetry = webClient.get() .uri("/users/{id}", userId) .retrieve() .bodyToMono(User.class) .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));性能优化建议
WebClient 默认使用 Reactor Netty 作为底层实现,可以配置连接池优化性能:
HttpClient httpClient = HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .responseTimeout(Duration.ofSeconds(5)) .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5))); WebClient webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .baseUrl("https://api.example.com") .build();对于高并发场景,建议使用连接池:
ConnectionProvider provider = ConnectionProvider.builder("myConnectionPool") .maxConnections(100) .pendingAcquireTimeout(Duration.ofSeconds(30)) .build(); HttpClient httpClient = HttpClient.create(provider);测试策略
测试 WebClient 时可以使用 MockWebServer 模拟 HTTP 服务:
@Test void testWebClient() throws IOException { MockWebServer server = new MockWebServer(); server.enqueue(new MockResponse() .setBody("{\"name\":\"test\"}") .addHeader("Content-Type", "application/json")); WebClient webClient = WebClient.create(server.url("/").toString()); Mono<User> userMono = webClient.get().retrieve().bodyToMono(User.class); User user = userMono.block(); assertEquals("test", user.getName()); server.shutdown(); }对于 HTTP Interface,可以使用 Mockito 进行测试:
@Mock private UserClient userClient; @Test void testUserClient() { when(userClient.getUser("1")).thenReturn(new User("1", "test")); User user = userClient.getUser("1"); assertEquals("test", user.getName()); }