RestClient
RestClient 是 Spring 6.1(Spring Boot 3.2)引入的同步 HTTP 客户端,提供流式 API,是 RestTemplate 的现代替代品。底层复用 RestTemplate 的连接工厂和消息转换器,无需引入额外依赖(spring-boot-starter-web 即可)。异步/响应式场景请使用 WebClient。
快速开始
无需额外依赖,Spring Boot 3.2+ 内置:
// 方式 1:静态工厂方法(适合一次性使用)
RestClient client = RestClient.create("https://api.example.com");
// 方式 2:Builder(推荐,可配置基础 URL、默认 Header、超时等)
RestClient client = RestClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
// 方式 3:注入为 Bean
@Bean
public RestClient restClient(RestClient.Builder builder) {
return builder
.baseUrl("https://api.example.com")
.build();
}Spring Boot 自动注册 RestClient.Builder Bean(已预配置消息转换器),推荐通过它创建。
连接超时配置
RestClient 复用 RestTemplate 底层连接工厂,超时配置方式相同:
@Bean
public RestClient restClient(RestClient.Builder builder) {
// 使用 Apache HttpClient 5(连接池)
PoolingHttpClientConnectionManager manager =
new PoolingHttpClientConnectionManager();
manager.setMaxTotal(200);
manager.setDefaultMaxPerRoute(50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(manager)
.evictIdleConnections(TimeValue.ofSeconds(30))
.build();
ClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return builder
.baseUrl("https://api.example.com")
.requestFactory(factory)
.build();
}也可以使用 JDK 21 的 JdkClientHttpRequestFactory(无需额外依赖):
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(3))
.build();
RestClient client = RestClient.builder()
.requestFactory(new JdkClientHttpRequestFactory(httpClient))
.build();基本请求
GET
@Service
@RequiredArgsConstructor
public class UserService {
private final RestClient restClient;
// 返回对象
public User getById(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.body(User.class);
}
// 泛型列表
public List<User> listAll() {
return restClient.get()
.uri("/users")
.retrieve()
.body(new ParameterizedTypeReference<List<User>>() {});
}
// 带查询参数(UriBuilder)
public List<User> search(String keyword, int page, int size) {
return restClient.get()
.uri(uriBuilder -> uriBuilder
.path("/users/search")
.queryParam("keyword", keyword)
.queryParam("page", page)
.queryParam("size", size)
.build())
.retrieve()
.body(new ParameterizedTypeReference<>() {});
}
// 获取完整响应(含状态码和 Header)
public ResponseEntity<User> getWithMeta(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.toEntity(User.class);
}
}POST
public Order createOrder(CreateOrderRequest request) {
return restClient.post()
.uri("/orders")
.contentType(MediaType.APPLICATION_JSON)
.body(request)
.retrieve()
.body(Order.class);
}
// 获取响应头中的 Location
public URI createAndGetLocation(CreateOrderRequest request) {
ResponseEntity<Void> response = restClient.post()
.uri("/orders")
.body(request)
.retrieve()
.toBodilessEntity();
return response.getHeaders().getLocation();
}PUT / PATCH / DELETE
public User updateUser(Long id, User user) {
return restClient.put()
.uri("/users/{id}", id)
.body(user)
.retrieve()
.body(User.class);
}
public User patchUser(Long id, Map<String, Object> fields) {
return restClient.patch()
.uri("/users/{id}", id)
.body(fields)
.retrieve()
.body(User.class);
}
public void deleteUser(Long id) {
restClient.delete()
.uri("/users/{id}", id)
.retrieve()
.toBodilessEntity();
}请求头管理
// 单次请求设置 Header
public User getWithAuth(Long id, String token) {
return restClient.get()
.uri("/users/{id}", id)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(User.class);
}
// 默认 Header(全局,在 Builder 中配置)
RestClient client = RestClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.defaultHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN")
.build();
// 动态默认 Header(每次请求前重新计算)
RestClient client = RestClient.builder()
.defaultHeaders(headers ->
headers.setBearerAuth(tokenProvider.getCurrentToken()))
.build();错误处理
retrieve() 在 4xx/5xx 时默认抛出 HttpClientErrorException / HttpServerErrorException,可用 onStatus 自定义:
public User getById(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
String body = new String(response.getBody().readAllBytes());
throw new BusinessException("客户端错误 " + response.getStatusCode() + ": " + body);
})
.onStatus(HttpStatusCode::is5xxServerError, (request, response) ->
new ServiceUnavailableException("下游服务异常: " + response.getStatusCode())
)
.body(User.class);
}全局错误处理可以结合 @ExceptionHandler 统一捕获 HttpStatusCodeException。
RequestInterceptor(拦截器)
Spring Boot 3.2+ 引入 ClientHttpRequestInterceptor,与 RestTemplate 共用同一接口:
// 认证拦截器
public class BearerTokenInterceptor implements ClientHttpRequestInterceptor {
private final TokenProvider tokenProvider;
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution)
throws IOException {
request.getHeaders().setBearerAuth(tokenProvider.getToken());
return execution.execute(request, body);
}
}
// 日志拦截器
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution)
throws IOException {
log.info("[HTTP] {} {}", request.getMethod(), request.getURI());
StopWatch watch = new StopWatch();
watch.start();
ClientHttpResponse response = execution.execute(request, body);
watch.stop();
log.info("[HTTP] {} {} → {} ({}ms)",
request.getMethod(), request.getURI(),
response.getStatusCode(), watch.getTotalTimeMillis());
return response;
}
}
// 注册
@Bean
public RestClient restClient(RestClient.Builder builder,
BearerTokenInterceptor authInterceptor,
LoggingInterceptor loggingInterceptor) {
return builder
.baseUrl("https://api.example.com")
.requestInterceptor(loggingInterceptor)
.requestInterceptor(authInterceptor)
.build();
}与 链路追踪 集成时,通过拦截器注入 X-Trace-Id 等请求头。
exchange — 低级访问
需要自定义响应解析或直接访问原始字节流时使用:
public Optional<User> findById(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.exchange((request, response) -> {
if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
return Optional.empty();
}
if (response.getStatusCode().is2xxSuccessful()) {
return Optional.of(
objectMapper.readValue(response.getBody(), User.class));
}
throw new ServiceException("意外状态码: " + response.getStatusCode());
});
}多服务实例管理
不同下游服务各自维护独立 RestClient Bean:
@Configuration
public class HttpClientConfig {
@Bean("paymentClient")
public RestClient paymentClient(RestClient.Builder builder) {
return builder
.baseUrl("https://payment.internal")
.requestInterceptor(new HmacSignInterceptor(paymentSecret))
.build();
}
@Bean("inventoryClient")
public RestClient inventoryClient(RestClient.Builder builder) {
return builder
.baseUrl("https://inventory.internal")
.defaultHeader("X-Service-Key", inventoryApiKey)
.build();
}
}
// 注入时用 @Qualifier 区分
@Service
public class OrderService {
@Autowired @Qualifier("paymentClient")
private RestClient paymentClient;
@Autowired @Qualifier("inventoryClient")
private RestClient inventoryClient;
}表单与文件上传
// 表单提交
public String submitForm(String name, String email) {
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.add("name", name);
form.add("email", email);
return restClient.post()
.uri("/form")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(form)
.retrieve()
.body(String.class);
}
// 文件上传(Multipart)
public String uploadFile(String filename, byte[] content) {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", new ByteArrayResource(content) {
@Override public String getFilename() { return filename; }
});
body.add("name", filename);
return restClient.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(body)
.retrieve()
.body(String.class);
}RestClient vs RestTemplate vs WebClient
| 特性 | RestClient | RestTemplate | WebClient |
|---|---|---|---|
| 编程模型 | 同步阻塞 | 同步阻塞 | 响应式(Mono/Flux) |
| API 风格 | 流式链式 | 方法重载 | 流式链式 |
| 流式推送支持 | 不支持 | 不支持 | 原生支持 |
| 最低 Spring Boot | 3.2 | 所有版本 | 2.x(需 WebFlux) |
| 推荐状态 | 推荐(同步) | 维护模式 | 推荐(异步) |
| 迁移成本 | 低(共享底层) | — | 中(需学习响应式) |
从 RestTemplate 迁移到 RestClient 几乎不需要改动连接池、序列化等配置,只需调整调用 API 风格。
相关链接
- RestTemplate — 旧版同步客户端,含连接池和错误处理详细配置
- WebClient — 响应式 HTTP 客户端,适合 WebFlux 和流式场景
- 链路追踪 — 通过拦截器注入 Trace Header
- 过滤器与拦截器 — 与 Spring MVC 拦截器的区别