Java调用REST接口案例

wen java案例 6

Java调用REST接口实战全解:从HttpClient到Spring RestTemplate与WebClient


📑 目录导读(Table of Contents)

  1. REST接口调用基础与Java生态演进
  2. 原生HttpURLConnection(零依赖)
  3. Apache HttpClient 5(经典稳定)
  4. Spring RestTemplate(同步利器)
  5. Spring WebClient(异步响应式)
  6. 核心痛点:JSON序列化、超时与重试策略
  7. 高频面试问答合集(Q&A)

REST接口调用基础与Java生态演进

REST(Representational State Transfer)已成为现代微服务间通信的事实标准,Java开发者调用REST接口时,常面临同步阻塞异步非阻塞的架构选型矛盾。

Java调用REST接口案例

根据2024年JVM生态报告,Spring RestTemplate仍是企业级项目使用率最高的客户端(占比42%),而WebClient在响应式架构中增速达31%,但无论选择哪种方案,都必须遵循HTTP规范(RFC 7231)——正确处理GET/POST/PUT/DELETE动词,以及状态码(200、201、404、500)等语义。

关键决策点:若项目已有Spring Boot,优先选RestTemplate(简单同步);若追求高并发吞吐,选WebClient(事件驱动);若不允许额外依赖,则用原生HttpURLConnection。


方案一:原生HttpURLConnection(零依赖)

适用场景:无任何框架的老牌项目或极简工具类。

public static String doGet(String urlStr) throws IOException {
    URL url = new URL(urlStr);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000); // 超时控制
    conn.setRequestProperty("Accept", "application/json");
    int code = conn.getResponseCode();
    if (code == 200) {
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream()))) {
            return reader.lines().collect(Collectors.joining());
        }
    } else {
        throw new RuntimeException("HTTP Error: " + code);
    } finally {
        conn.disconnect();
    }
}

局限性:需手动管理流、连接池缺失、代码冗长,实际生产中强烈建议升级。


方案二:Apache HttpClient 5(经典稳定)

优势:连接池、重试机制、SSL配置成熟,这是Apache基金会的旗舰项目,迭代超过20年。

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3.1</version>
</dependency>

核心代码片段

try (CloseableHttpClient client = HttpClients.custom()
        .setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
                .setMaxTotal(100).setDefaultMaxPerRoute(20).build())
        .setRetryStrategy(new DefaultRetryStrategy(3, TimeValue.ofSeconds(1)))
        .build()) {
    HttpPost post = new HttpPost("https://api.example.com/v1/users");
    post.setHeader("Content-Type", "application/json");
    post.setEntity(new StringEntity("{\"name\":\"Alice\"}", ContentType.APPLICATION_JSON));
    try (CloseableHttpResponse resp = client.execute(post)) {
        return EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
    }
}

注意:5.x版本API与4.x有较大差异,需留意包名变更。


方案三:Spring RestTemplate(同步利器)

Spring生态中最常用的高层封装,自动处理JSON/XML转换。重要提示:Spring 6.0及Spring Boot 3.x中已标记为“维护模式”,但社区存量巨大。

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(10000);
        return new RestTemplate(factory);
    }
}
// 调用示例
ResponseEntity<User> response = restTemplate.postForEntity(
    "https://api.example.com/users",   // URL
    new HttpEntity<>(user, headers),   // 请求体+头
    User.class                         // 返回类型
);

最佳实践:用ParameterizedTypeReference处理泛型列表:

ResponseEntity<List<User>> list = restTemplate.exchange(
    url, HttpMethod.GET, null, new ParameterizedTypeReference<List<User>>() {});

方案四:Spring WebClient(异步响应式)

基于Reactor,非阻塞IO,单线程可处理数千并发,适合网关或微服务聚合层。

WebClient client = WebClient.builder()
    .baseUrl("https://api.example.com")
    .defaultHeader("Authorization", "Bearer token")
    .build();
Mono<User> userMono = client.get()
    .uri("/users/{id}", 123)
    .retrieve()
    .bodyToMono(User.class);
// 阻塞获取结果(仅限测试/同步场景)
User user = userMono.block(Duration.ofSeconds(5));

进阶:使用exchangeToMono处理非2xx错误码,注意WebClient对象应复用(线程安全),切勿每次请求新建。


核心痛点:JSON序列化、超时与重试策略

  1. JSON序列化:使用Jackson的ObjectMapper时,需注意LocalDateTime等Java8时间类型会报错,需注册JavaTimeModule

    ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
  2. 超时配置:始终设置connectTimeout(连接超时)与readTimeout(读超时),否则在高并发下,线程会因慢接口耗尽。

  3. 重试策略:只重试幂等请求(GET/HEAD/PUT),对POST要谨慎,可能导致重复数据,使用Spring的RetryTemplate或Apache的DefaultRetryStrategy

  4. 统一封装:建议将调用逻辑封装成ApiClient类,统一处理签名、日志、熔断(可结合Resilience4j)。


高频面试问答合集(Q&A)

问1:RestTemplate与WebClient的核心区别? 答:RestTemplate是同步阻塞模型,每个请求占用一个线程;WebClient是异步非阻塞,基于Netty事件循环,用少量线程支撑高并发,从Spring 5开始官方推荐WebClient,但RestTemplate依旧够小规模系统使用。

问2:调用REST接口时,如何优雅处理连接超时? 答:第一层设置HTTP客户端超时(如setConnectTimeout);第二层使用HystrixCommandResilience4jTimeLimiter做全局超时;第三层在异步场景用block(Duration)限定等待时间。

问3:JSON字段与Java对象属性命名不一致怎么办? 答:使用@JsonProperty("another_name")注解,若使用Jackson,也可配置PropertyNamingStrategies.SNAKE_CASE策略自动映射下划线风格。

问4:HTTP 413(Payload Too Large)错误通常由什么导致? 答:接收方限制了请求体大小,如果是Spring Boot,需设置spring.servlet.multipart.max-request-size;若是Nginx代理,可能需调整client_max_body_size配置。


无论选择哪种调用方案,务必遵循“连接池化、超时控制、日志留痕、错误分类”四大铁律,在微服务拆分的当下,WebClient的响应式优势日益明显,但对于80%的CRUD应用,RestTemplate依然是稳健之选,实践出真知——建议在本地搭建Mock Server(如WireMock)进行压测对比,找到最适合业务场景的方案。

抱歉,评论功能暂时关闭!