RestTemplate

RestTemplate 是 Spring 提供的同步 HTTP 客户端,封装了 HTTP 请求的发送与响应解析。Spring Boot 3.x 中仍可使用,但官方已将其标记为维护模式,新项目建议使用 WebClient(支持响应式)或 Spring Boot 3.2+ 引入的 RestClient(同步,更简洁)。


快速开始

Spring Boot 不自动注入 RestTemplate,需手动声明 Bean:

@Configuration
public class RestTemplateConfig {
 
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
            .connectTimeout(Duration.ofSeconds(3))
            .readTimeout(Duration.ofSeconds(10))
            .build();
    }
}

基本请求

GET

@Service
public class WeatherService {
 
    private final RestTemplate restTemplate;
    private static final String BASE_URL = "https://api.example.com";
 
    // 返回字符串
    public String getRaw(String city) {
        return restTemplate.getForObject(BASE_URL + "/weather?city={city}",
                                         String.class, city);
    }
 
    // 返回对象(自动 JSON 反序列化)
    public WeatherResponse getWeather(String city) {
        return restTemplate.getForObject(BASE_URL + "/weather?city={city}",
                                         WeatherResponse.class, city);
    }
 
    // 获取完整响应(含状态码和 Header)
    public ResponseEntity<WeatherResponse> getWithMeta(String city) {
        return restTemplate.getForEntity(BASE_URL + "/weather?city={city}",
                                          WeatherResponse.class, city);
    }
 
    // 泛型:List<User> 需用 ParameterizedTypeReference
    public List<User> listUsers() {
        ResponseEntity<List<User>> response = restTemplate.exchange(
            BASE_URL + "/users",
            HttpMethod.GET,
            null,
            new ParameterizedTypeReference<List<User>>() {}
        );
        return response.getBody();
    }
}

POST

// 发送 JSON 请求体,返回对象
public OrderResponse createOrder(CreateOrderRequest request) {
    return restTemplate.postForObject(BASE_URL + "/orders",
                                       request, OrderResponse.class);
}
 
// 获取响应头中的 Location(创建资源后常见)
public URI createAndGetLocation(CreateOrderRequest request) {
    ResponseEntity<Void> response = restTemplate.postForLocation(
        BASE_URL + "/orders", request);
    return response.getHeaders().getLocation();
}

PUT / DELETE

// PUT 无返回值
public void updateUser(Long id, User user) {
    restTemplate.put(BASE_URL + "/users/{id}", user, id);
}
 
// DELETE
public void deleteUser(Long id) {
    restTemplate.delete(BASE_URL + "/users/{id}", id);
}

exchange — 通用方法

exchange 可完整控制请求方法、Header、请求体和响应类型:

public UserResponse getUserWithAuth(Long id, String token) {
    HttpHeaders headers = new HttpHeaders();
    headers.setBearerAuth(token);
    headers.setAccept(List.of(MediaType.APPLICATION_JSON));
 
    HttpEntity<Void> entity = new HttpEntity<>(headers);
 
    ResponseEntity<UserResponse> response = restTemplate.exchange(
        BASE_URL + "/users/{id}",
        HttpMethod.GET,
        entity,
        UserResponse.class,
        id
    );
 
    if (!response.getStatusCode().is2xxSuccessful()) {
        throw new ServiceException("获取用户失败: " + response.getStatusCode());
    }
    return response.getBody();
}

请求头与参数构建

// URI 模板变量(Map 形式)
Map<String, Object> params = Map.of("city", "Beijing", "lang", "zh");
String url = BASE_URL + "/weather?city={city}&lang={lang}";
WeatherResponse resp = restTemplate.getForObject(url, WeatherResponse.class, params);
 
// 使用 UriComponentsBuilder 构建复杂 URL
URI uri = UriComponentsBuilder.fromHttpUrl(BASE_URL + "/search")
    .queryParam("keyword", "spring boot")
    .queryParam("page", 1)
    .queryParam("size", 20)
    .build()
    .toUri();
 
String result = restTemplate.getForObject(uri, String.class);

拦截器

统一处理 Header 注入(如 Token)、日志记录、签名等:

@Component
public class AuthInterceptor implements ClientHttpRequestInterceptor {
 
    private final TokenProvider tokenProvider;
 
    @Override
    public ClientHttpResponse intercept(
            HttpRequest request, byte[] body,
            ClientHttpRequestExecution execution) throws IOException {
 
        request.getHeaders().setBearerAuth(tokenProvider.getToken());
        ClientHttpResponse response = execution.execute(request, body);
 
        // 记录请求日志
        log.info("[HTTP] {} {} → {}", request.getMethod(),
                  request.getURI(), response.getStatusCode());
        return response;
    }
}
 
// 注册拦截器
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder,
                                  AuthInterceptor authInterceptor) {
    return builder
        .additionalInterceptors(authInterceptor)
        .connectTimeout(Duration.ofSeconds(3))
        .readTimeout(Duration.ofSeconds(10))
        .build();
}

连接池(Apache HttpClient)

默认使用 JDK HttpURLConnection(无连接池),高并发场景建议切换到 Apache HttpClient:

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
</dependency>
@Bean
public RestTemplate restTemplate() {
    PoolingHttpClientConnectionManager manager =
        new PoolingHttpClientConnectionManager();
    manager.setMaxTotal(200);           // 总连接数
    manager.setDefaultMaxPerRoute(50);  // 每个路由最大连接数
 
    CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(manager)
        .evictIdleConnections(TimeValue.ofSeconds(30))
        .build();
 
    HttpComponentsClientHttpRequestFactory factory =
        new HttpComponentsClientHttpRequestFactory(httpClient);
    factory.setConnectTimeout(3000);
    factory.setReadTimeout(10000);
 
    return new RestTemplate(factory);
}

错误处理

默认 DefaultResponseErrorHandler 在 4xx/5xx 时抛出 HttpClientErrorException / HttpServerErrorException,可自定义:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
        .errorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse response) throws IOException {
                return response.getStatusCode().isError();
            }
 
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                HttpStatusCode status = response.getStatusCode();
                String body = StreamUtils.copyToString(
                    response.getBody(), StandardCharsets.UTF_8);
 
                if (status.is4xxClientError()) {
                    throw new ClientException(status.value(), body);
                }
                if (status.is5xxServerError()) {
                    throw new ServerException(status.value(), body);
                }
            }
        })
        .build();
}

重试

配合 Spring Retry 实现自动重试(适合幂等 GET 请求):

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
@EnableRetry
@Configuration
public class RetryConfig {}
 
@Service
public class RemoteService {
 
    @Retryable(
        retryFor = { ResourceAccessException.class, ServerException.class },
        maxAttempts = 3,
        backoff = @Backoff(delay = 500, multiplier = 2)
    )
    public String fetchData(String id) {
        return restTemplate.getForObject(BASE_URL + "/data/{id}",
                                          String.class, id);
    }
 
    @Recover
    public String recover(Exception e, String id) {
        log.error("重试耗尽,id={}", id, e);
        return null;
    }
}

RestClient(Spring Boot 3.2+ 推荐)

RestClient 是 Spring 6.1 引入的同步替代品,API 更简洁,与 RestTemplate 底层共享连接工厂:

RestClient client = RestClient.builder()
    .baseUrl(BASE_URL)
    .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token)
    .build();
 
// GET
WeatherResponse weather = client.get()
    .uri("/weather?city={city}", "Beijing")
    .retrieve()
    .body(WeatherResponse.class);
 
// POST
OrderResponse order = client.post()
    .uri("/orders")
    .contentType(MediaType.APPLICATION_JSON)
    .body(request)
    .retrieve()
    .body(OrderResponse.class);

RestTemplate vs WebClient vs RestClient

特性RestTemplateRestClientWebClient
编程模型同步阻塞同步阻塞异步/响应式
Spring Boot 版本所有版本3.2+2.x+ (WebFlux)
API 风格方法重载多流式链式流式链式
状态维护模式推荐推荐(响应式)

相关链接