
一、基础定位与版本说明
-
诞生背景
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-web,Spring Framework 7 自动包含 RestClient:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>7.0.0</version>
</dependency>
如需连接池、超时精细化控制,引入Apache HttpClient:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</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) -
URI:
uri()支持占位符、参数构建器 -
请求配置: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. 动态拼接查询参数
List<OrderDTO> orders = restClient.get()
.uri(uriBuilder-> uriBuilder
.path("/order/list")
.queryParam("status", 1)
.queryParam("page", 1)
.queryParam("size", 20)
.build())
.retrieve()
.body(newParameterizedTypeReference<List<OrderDTO>>() {});
2. POST JSON 请求(传对象)
CreateOrderReq req = newCreateOrderReq();
req.setUserId(1001);
req.setAmount(newBigDecimal("99.9"));
ResponseEntity<OrderResp> 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-urlencoded
MultiValueMap<String, 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覆盖 -
简洁API:
body()/toEntity()/toBodilessEntity()// 直接转实体
UserDTO dto = restClient.get().uri("/user/1").retrieve().body(UserDTO.class);
// 返回完整ResponseEntity
ResponseEntityentity = 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 子类:
-
HttpClientErrorException:4xx(NotFound/BadRequest等) -
HttpServerErrorException:5xx -
ResourceAccessException:网络超时、连接失败、DNS错误 -
UnknownContentTypeException:返回类型不匹配、序列化失败
2. 三种异常拦截方式
方式1:Builder全局统一异常(所有请求生效)
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);
}
七、拦截器实现(日志、链路追踪、重试)
实现 ClientHttpRequestInterceptor,Builder注入 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 自动按类路径优先级选择工厂:
-
HttpComponentsClientHttpRequestFactory(Apache HttpClient,推荐,支持连接池) -
JettyClientHttpRequestFactory -
JdkClientHttpRequestFactory(JDK11+内置HttpClient) -
SimpleClientHttpRequestFactory(JDK 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);}
-
注册代理Bean
@Bean
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 迁移对照表
| RestTemplate | RestClient 等价写法 |
|---|---|
| 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() |
| setErrorHandler | builder.defaultStatusHandler / 单次onStatus |
| addInterceptors | builder.requestInterceptor() |
十一、最佳实践总结
-
统一单例Bean:全局只创建一个RestClient,复用连接池;
-
优先builder全局配置:baseUrl、默认Header、拦截器、全局异常统一配置;
-
高并发必须使用Apache HttpClient连接池,禁用Simple工厂;
-
简单请求用
retrieve(),下载流/自定义异常用exchange(); -
大量微服务接口推荐 HTTP Interface + RestClient,简化代码;
-
所有远程调用捕获
RestClientException,区分网络超时、4xx参数、5xx服务异常; -
存量项目逐步替换RestTemplate,新项目禁止使用RestTemplate。