一、基础定位与版本说明诞生背景RestClient自 Spring 6.1 引入Spring 7 正式定为同步HTTP客户端首选RestTemplate被标记废弃新项目强制推荐 RestClient。核心优势流式链式API风格贴近 WebClient易上手线程安全单例Bean全局复用统一底层ClientHttpRequestFactory无缝切换Apache HttpClient/JDK HttpClient/Jetty内置结构化JSON序列化、灵活状态异常处理、拦截器、全局默认Header/Cookie支持retrieve()简易模式、exchange()底层原始响应模式完美搭配 Spring 7 HTTP Interface 声明式接口客户端。与其他客户端对比RestClient | Spring MVC 同步业务、微服务普通调用 | 阻塞 | 官方首选WebClient | WebFlux响应式、高并发流式 | 非阻塞 | 响应式专用RestTemplate | 老旧存量项目 | 阻塞 | Deprecated不再新增特性 |二、环境依赖仅需spring-webSpring Framework 7 自动包含 RestClientdependency groupIdorg.springframework/groupId artifactIdspring-web/artifactId version7.0.0/version /dependency如需连接池、超时精细化控制引入Apache HttpClientdependency groupIdorg.apache.httpcomponents/groupId artifactIdhttpclient/artifactId /dependency三、RestClient 实例创建3种方式1. 最简快速创建默认JDK底层// 无全局配置临时使用 RestClient restClient RestClient.create();2. Builder 完整自定义项目标准Bean写法推荐import org.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.http.client.HttpComponentsClientHttpRequestFactory; importorg.springframework.web.client.RestClient; importjava.time.Duration; Configuration public class RestClientConfig { Bean public RestClient restClient() { // 底层工厂ApacheHttpClient 支持连接池、超时 HttpComponentsClientHttpRequestFactoryfactory newHttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(Duration.ofSeconds(5)); // 连接超时 factory.setReadTimeout(Duration.ofSeconds(15)); // 读取超时 factory.setConnectionRequestTimeout(Duration.ofSeconds(3)); // 从连接池获取连接超时 returnRestClient.builder() // 全局基础地址后续请求可只写路径 .baseUrl(https://api.example.com) // 全局路径变量 .defaultUriVariables(Map.of(version, v1)) // 全局默认请求头 .defaultHeader(Content-Type, application/json) .defaultHeader(User-Agent, spring7-restclient) // 全局Cookie .defaultCookie(token, global-token-xxx) // 请求拦截器日志、鉴权、重试统一处理 .requestInterceptor(newLogInterceptor()) // 请求初始化器统一修改请求 .requestInitializer(request- request.getHeaders().set(Trace-Id, UUID.randomUUID().toString())) // 自定义消息转换器 .messageConverters(converters- { // 追加自定义Jackson转换器 }) // 全局统一异常处理 .defaultStatusHandler( HttpStatusCode::isError, (req, res) - { throw new BusinessApiException(res.getStatusCode(), 上游接口异常); } ) // 指定底层HTTP工厂 .requestFactory(factory) .build(); } }3. 从 RestTemplate 迁移创建RestTemplate oldTemplate new RestTemplate(); RestClient restClient RestClient.create(oldTemplate);四、核心请求语法全示例通用流程方法get()/post()/put()/delete()/patch()/method(HttpMethod)URIuri()支持占位符、参数构建器请求配置header、cookie、body、multipart响应分支二选一retrieve()日常使用内置状态码异常exchange()底层原始响应完全自定义异常逻辑1. GET 请求路径变量 查询参数// 1. 基础路径变量 UserDTOuser restClient.get() .uri(/user/{id}, 1001) .header(Authorization, Bearerxxx) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, (req, resp) - { throw new UserNotFoundException(用户不存在); }) .body(UserDTO.class); // 2. 动态拼接查询参数 ListOrderDTO orders restClient.get() .uri(uriBuilder- uriBuilder .path(/order/list) .queryParam(status, 1) .queryParam(page, 1) .queryParam(size, 20) .build()) .retrieve() .body(newParameterizedTypeReferenceListOrderDTO() {});2. POST JSON 请求传对象CreateOrderReq req newCreateOrderReq(); req.setUserId(1001); req.setAmount(newBigDecimal(99.9)); ResponseEntityOrderResp respEntity restClient.post() .uri(/order/create) .body(req) // 自动序列化为JSON .retrieve() .toEntity(OrderResp.class); OrderRespbody respEntity.getBody(); HttpHeadersheaders respEntity.getHeaders(); HttpStatusCodestatus respEntity.getStatusCode();3. PUT / DELETE// PUT 更新 restClient.put() .uri(/user/{id}, 1001) .body(updateDTO) .retrieve() .toBodilessEntity(); // DELETE 无返回体 restClient.delete() .uri(/user/{id}, 1001) .retrieve() .toBodilessEntity();4. Multipart 文件上传文件表单JSON混合MultipartBodyBuilder multipartBuilder newMultipartBodyBuilder(); // 文件 multipartBuilder.part(file, newFileSystemResource(/tmp/test.jpg)) .filename(upload.jpg); // 普通表单字段 multipartBuilder.part(desc, 测试图片); // JSON子对象 multipartBuilder.part(meta, {\type\:\img\}, MediaType.APPLICATION_JSON); StringuploadResult restClient.post() .uri(/upload/file) .contentType(MediaType.MULTIPART_FORM_DATA) .body(multipartBuilder.build()) .retrieve() .body(String.class);5. Form 表单提交 application/x-www-form-urlencodedMultiValueMapString, String formData new LinkedMultiValueMap(); formData.add(username, admin); formData.add(password, 123456); TokenResp token restClient.post() .uri(/login) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(formData) .retrieve() .body(TokenResp.class);五、两种响应模式retrieve() vs exchange()1. retrieve()90%业务场景使用自动判定4xx/5xx并抛出异常可局部onStatus覆盖简洁APIbody()/toEntity()/toBodilessEntity()// 直接转实体 UserDTO dto restClient.get().uri(/user/1).retrieve().body(UserDTO.class); // 返回完整ResponseEntity ResponseEntityUserDTO entity restClient.get().uri(/user/1).retrieve().toEntity(UserDTO.class); // 无响应体 restClient.delete().uri(/user/1).retrieve().toBodilessEntity();2. exchange()高级底层场景不会自动抛状态异常完全手动处理响应适合404/500需要正常读取返回体下载二进制流、手动读取响应流自定义所有错误逻辑// 手动处理所有状态码 RestClient.ResponseSpecresponseSpec restClient.get() .uri(/user/{id}, 9999) .exchange(); // 手动判断状态 if (responseSpec.statusCode().is2xxSuccessful()) { UserDTO user responseSpec.body(UserDTO.class); } elseif (responseSpec.statusCode().value() 404) { // 不存在返回空 return null; } else { throw new RuntimeException(请求异常 responseSpec.statusCode()); } // 二进制文件下载 InputStreaminputStream restClient.get() .uri(/file/download) .exchange() .body(InputStream.class);六、异常处理体系1. 统一异常父类所有异常均为RestClientException子类HttpClientErrorException4xxNotFound/BadRequest等HttpServerErrorException5xxResourceAccessException网络超时、连接失败、DNS错误UnknownContentTypeException返回类型不匹配、序列化失败2. 三种异常拦截方式方式1Builder全局统一异常所有请求生效RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (req, res) - { log.error(上游异常 status{}, res.getStatusCode()); throw new ApiCallException(res.getStatusCode(), res.body(String.class)); }) .build();方式2单次请求局部onStatus覆盖全局restClient.get() .uri(/xxx) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, (req, resp) - { throw new ClientParamException(参数错误); }) .onStatus(HttpStatusCode::is5xxServerError, (req, resp) - { throw new RemoteServiceException(服务宕机); }) .body(String.class);方式3业务层try-catch捕获try { return restClient.get().uri(/user/1).retrieve().body(UserDTO.class); } catch (HttpClientErrorException.NotFound e) { log.warn(用户不存在); return null; } catch (ResourceAccessException e) { throw new RuntimeException(第三方接口超时); } catch (RestClientException e) { throw new RuntimeException(调用接口失败, e); }七、拦截器实现日志、链路追踪、重试实现ClientHttpRequestInterceptorBuilder注入requestInterceptor()示例全局请求日志拦截器Slf4j public class LogInterceptor implements ClientHttpRequestInterceptor { Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { log.info(请求地址:{} 方法:{} 请求体:{}, request.getURI(), request.getMethod(), new String(body, StandardCharsets.UTF_8)); ClientHttpResponse response execution.execute(request, body); log.info(响应状态:{}, response.getStatusCode()); return response; } }重试拦截器仅5xx自动重试public classRetryInterceptorimplementsClientHttpRequestInterceptor { private final int maxRetry; public RetryInterceptor(int maxRetry) { this.maxRetry maxRetry; } Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { inttimes 0; while (true) { try { return execution.execute(request, body); } catch (HttpServerErrorExceptione) { times; if (times maxRetry) throw e; log.warn(5xx异常第{}次重试, times); } } } }八、底层请求工厂切换与连接池配置Spring7 自动按类路径优先级选择工厂HttpComponentsClientHttpRequestFactoryApache HttpClient推荐支持连接池JettyClientHttpRequestFactoryJdkClientHttpRequestFactoryJDK11内置HttpClientSimpleClientHttpRequestFactoryJDK HttpURLConnection无连接池Apache HttpClient 连接池完整配置Bean public ClientHttpRequestFactory httpRequestFactory() { // 连接池管理器 PoolingHttpClientConnectionManagerpoolManager newPoolingHttpClientConnectionManager(); poolManager.setMaxTotal(200); // 最大总连接数 poolManager.setDefaultMaxPerRoute(50);// 单域名最大连接 CloseableHttpClienthttpClient HttpClients.custom() .setConnectionManager(poolManager) .evictIdleConnections(Duration.ofSeconds(30)) // 清理空闲连接 .build(); HttpComponentsClientHttpRequestFactoryfactory newHttpComponentsClientHttpRequestFactory(httpClient); factory.setConnectTimeout(Duration.ofSeconds(5)); factory.setReadTimeout(Duration.ofSeconds(10)); factory.setConnectionRequestTimeout(Duration.ofSeconds(3)); returnfactory; } Bean public RestClient restClient(ClientHttpRequestFactory factory) { returnRestClient.builder() .requestFactory(factory) .build(); }九、Spring7 HTTP Interface 声明式调用搭配RestClient类似Feign零冗余链式代码底层复用RestClient定义接口HttpExchange(/api/v1) public interface UserApiClient { GetExchange(/user/{id}) UserDTO getUser(PathVariable Long id); PostExchange(/user/create) UserDTO createUser(RequestBody CreateUserReq req); }注册代理BeanBean public UserApiClient userApiClient(RestClient restClient) { HttpServiceProxyFactory factory HttpServiceProxyFactory .builderFor(restClient) .build(); return factory.createClient(UserApiClient.class); }直接注入调用Service public class UserService { privatefinalUserApiClientuserApiClient; publicUserService(UserApiClientuserApiClient) { this.userApiClient userApiClient; } publicUserDTOget(Longid) { return userApiClient.getUser(id); } }十、RestTemplate 迁移对照表RestTemplateRestClient 等价写法getForObject(url, Cls, args)get().uri(url, args).retrieve().body(Cls)postForEntity(url, req, Cls)post().uri(url).body(req).retrieve().toEntity(Cls)exchange(url, POST, entity, Cls)post().uri(url).headers(entity.getHeaders()).body(entity.getBody()).exchange()setErrorHandlerbuilder.defaultStatusHandler / 单次onStatusaddInterceptorsbuilder.requestInterceptor()十一、最佳实践总结统一单例Bean全局只创建一个RestClient复用连接池优先builder全局配置baseUrl、默认Header、拦截器、全局异常统一配置高并发必须使用Apache HttpClient连接池禁用Simple工厂简单请求用retrieve()下载流/自定义异常用exchange()大量微服务接口推荐HTTP Interface RestClient简化代码所有远程调用捕获RestClientException区分网络超时、4xx参数、5xx服务异常存量项目逐步替换RestTemplate新项目禁止使用RestTemplate。