RestClient

返回 Spring Boot 基础

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

特性RestClientRestTemplateWebClient
编程模型同步阻塞同步阻塞响应式(Mono/Flux)
API 风格流式链式方法重载流式链式
流式推送支持不支持不支持原生支持
最低 Spring Boot3.2所有版本2.x(需 WebFlux)
推荐状态推荐(同步)维护模式推荐(异步)
迁移成本低(共享底层)中(需学习响应式)

RestTemplate 迁移到 RestClient 几乎不需要改动连接池、序列化等配置,只需调整调用 API 风格。


相关链接