spring boot 外部 API 调用 ----22

 

 

覆盖 Spring Boot 3 官方推荐的两种调用方式:RestClient(命令式灵活调用) + HTTP Service Client(声明式接口调用)。基于公开测试接口 JSONPlaceholder 演示,无需额外搭建服务,复制即可运行,严格遵循生产级最佳实践。

一、项目结构

plaintext
 
 
com.example.apicall
├── ApiCallApplication.java        # 启动类
├── entity
│   └── Post.java                  # 通用响应实体
├── config
│   └── ApiClientConfig.java       # 客户端统一配置
├── client
│   └── JsonPlaceholderApi.java    # 声明式HTTP接口
└── service
    ├── RestClientDemoService.java # RestClient用法演示
    └── HttpServiceDemoService.java# 声明式调用演示
└── controller
    └── ApiTestController.java     # 测试接口

 

 

二、pom.xml 核心依赖

Spring Boot 3.x 内置 RestClientHTTP Service,只需 Web 基础依赖即可,无需额外第三方包。
xml
 
 
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>rest-api-call-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Web 基础:内置 RestClient + HTTP Service 能力 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Lombok 简化代码 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

 

 

三、通用响应实体

模拟外部接口返回的帖子数据,对接公开测试接口 jsonplaceholder.typicode.com
java
 
运行
 
 
package com.example.apicall.entity;

import lombok.Data;

@Data
public class Post {
    private Integer id;
    private Integer userId;
    private String title;
    private String body;
}
 

 


四、第一部分:RestClient 灵活调用(对应 227、228 集)

Spring 6 推出的现代 HTTP 客户端,替代旧版 RestTemplate,流式 API 更简洁,支持同步调用。

1. 配置类(生产级最佳实践)

统一配置 baseUrl、超时、默认请求头,注册为 Spring Bean,避免每次调用重复创建。
java
 
运行
 
 
package com.example.apicall.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestClient;

@Configuration
public class ApiClientConfig {

    /**
     * RestClient 客户端 Bean
     * 对应227集:现代REST调用方式
     * 最佳实践:预配置baseUrl、超时时间、默认头,复用实例
     */
    @Bean
    public RestClient jsonPlaceholderRestClient() {
        // 配置超时:连接3秒,读取5秒,防止外部接口卡住
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(3000);
        factory.setReadTimeout(5000);

        return RestClient.builder()
                .baseUrl("https://jsonplaceholder.typicode.com")
                .defaultHeader("Content-Type", "application/json")
                .requestFactory(factory)
                .build();
    }
}
 

 

2. 业务服务类(基础 + 高级用法)

java
 
运行
 
 
package com.example.apicall.service;

import com.example.apicall.entity.Post;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

import java.util.List;

@Service
@RequiredArgsConstructor
public class RestClientDemoService {

    private final RestClient jsonPlaceholderRestClient;

    /**
     * 1. 基础GET:根据ID查询单条数据
     * 对应227集:简单GET调用
     */
    public Post getPostById(Integer id) {
        return jsonPlaceholderRestClient.get()
                .uri("/posts/{id}", id)    // 路径参数占位符
                .retrieve()                 // 发起请求并解析响应
                .body(Post.class);          // 响应体自动转成Java对象
    }

    /**
     * 2. 基础GET:查询列表
     */
    public List<Post> getAllPosts() {
        return jsonPlaceholderRestClient.get()
                .uri("/posts")
                .retrieve()
                .body(List.class);
    }

    /**
     * 3. POST请求:提交数据
     * 对应228集:超出简单GET的高级用法
     */
    public Post createPost(Post post) {
        return jsonPlaceholderRestClient.post()
                .uri("/posts")
                .body(post)                 // 请求体
                .retrieve()
                .body(Post.class);
    }

    /**
     * 4. 高级:自定义异常处理 + 获取响应状态
     * 对应228集:错误处理、状态码判断
     */
    public Post getPostWithErrorHandle(Integer id) {
        return jsonPlaceholderRestClient.get()
                .uri("/posts/{id}", id)
                .retrieve()
                // 4xx/5xx 状态码自定义处理
                .onStatus(status -> status.is4xxClientError(), (req, resp) -> {
                    throw new RuntimeException("客户端错误,状态码:" + resp.getStatusCode());
                })
                .onStatus(status -> status.is5xxServerError(), (req, resp) -> {
                    throw new RuntimeException("外部服务异常");
                })
                .body(Post.class);
    }
}

 

 

五、第二部分:HTTP Service Client 声明式调用(对应 229~231 集)

类似 OpenFeign 的声明式写法,只需要定义接口,Spring 自动生成实现,代码更简洁,维护性更强,是固定接口调用的首选方案。

1. 声明式接口定义

java
 
运行
 
 
package com.example.apicall.client;

import com.example.apicall.entity.Post;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.PostExchange;
import org.springframework.web.service.annotation.HttpExchange;

import java.util.List;

/**
 * 声明式HTTP接口
 * 对应229集:声明式REST调用
 * 只写接口和注解,Spring自动生成实现类
 */
@HttpExchange("/posts")
public interface JsonPlaceholderApi {

    // 查询单条
    @GetExchange("/{id}")
    Post getPostById(Integer id);

    // 查询列表
    @GetExchange
    List<Post> listPosts();

    // 新建
    @PostExchange
    Post createPost(Post post);
}
 

 

2. 注册为 Spring Bean(配置类补充)

ApiClientConfig.java 中追加 Bean 定义,通过代理工厂创建接口实现。
java
 
运行
 
 
/**
 * HTTP Service 声明式客户端 Bean
 * 对应230集:消费REST API
 * 对应231集:按服务分组管理,不同服务对应不同接口Bean
 */
@Bean
public JsonPlaceholderApi jsonPlaceholderApi(RestClient.Builder builder) {
    // 复用RestClient的配置能力作为底层客户端
    RestClient restClient = builder
            .baseUrl("https://jsonplaceholder.typicode.com")
            .defaultHeader("Content-Type", "application/json")
            .build();

    // 创建代理工厂,生成接口实现
    return HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(restClient))
            .build()
            .createClient(JsonPlaceholderApi.class);
}
 

 

3. 使用示例

java
 
运行
 
 
package com.example.apicall.service;

import com.example.apicall.client.JsonPlaceholderApi;
import com.example.apicall.entity.Post;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class HttpServiceDemoService {

    // 直接注入声明式接口,和调用本地方法一样
    private final JsonPlaceholderApi jsonPlaceholderApi;

    public Post getPost(Integer id) {
        return jsonPlaceholderApi.getPostById(id);
    }

    public List<Post> listPosts() {
        return jsonPlaceholderApi.listPosts();
    }

    public Post addPost(Post post) {
        return jsonPlaceholderApi.createPost(post);
    }
}

 

 
💡 分组说明(对应 231 集):如果有多个外部服务,就定义多个接口 + 多个 Bean,每个对应不同的 baseUrl,按服务分组管理,结构清晰互不干扰。

六、测试接口 Controller

java
 
运行
 
 
package com.example.apicall.controller;

import com.example.apicall.entity.Post;
import com.example.apicall.service.HttpServiceDemoService;
import com.example.apicall.service.RestClientDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/test")
@RequiredArgsConstructor
public class ApiTestController {

    private final RestClientDemoService restClientDemoService;
    private final HttpServiceDemoService httpServiceDemoService;

    // ===== RestClient 测试 =====
    @GetMapping("/rest/post/{id}")
    public Post restGetPost(@PathVariable Integer id) {
        return restClientDemoService.getPostById(id);
    }

    @PostMapping("/rest/post")
    public Post restCreatePost(@RequestBody Post post) {
        return restClientDemoService.createPost(post);
    }

    // ===== HTTP Service 测试 =====
    @GetMapping("/http/post/{id}")
    public Post httpGetPost(@PathVariable Integer id) {
        return httpServiceDemoService.getPost(id);
    }

    @GetMapping("/http/posts")
    public List<Post> httpListPosts() {
        return httpServiceDemoService.listPosts();
    }
}
 

 


七、启动类

java
 
运行
 
 
package com.example.apicall;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiCallApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiCallApplication.class, args);
    }
}

 

 

八、课程对应表

表格
 
集数核心内容对应代码
226 REST API 消费者概念 前置说明 + 整体设计
227 RestClient 现代 REST 调用方式 RestClient 配置 + 基础 GET 方法
228 RestClient 高级用法(POST、错误处理) createPost、异常状态码处理
229 HTTP Service Client 声明式调用介绍 JsonPlaceholderApi 接口定义
230 用 HTTP Service 消费 REST 接口 接口代理 Bean + 服务层调用
231 HTTP Service 分组管理 按服务拆分接口 + 独立 Bean 的设计

九、最佳实践总结

  1. 选型原则
    • 固定接口、标准化调用 → 优先用 HTTP Service,代码最少、维护性最强
    • 动态 URL、复杂请求、需要精细控制 → 用 RestClient
    • 旧项目兼容可保留 RestTemplate,新项目直接上 RestClient
  2. 配置规范
    • 统一抽取 baseUrl、超时、公共头到配置类,不要硬编码在业务代码里
    • 必须设置连接超时和读取超时,防止外部接口故障拖垮自身服务
    • 不同外部服务分组配置,各自独立 Bean,避免混淆
  3. 异常与容错
    • 对 4xx、5xx 状态码做分类处理,不要全吞异常
    • 生产环境建议配合重试、熔断组件(如 Resilience4j)
    • 外部调用必须打日志,记录入参、出参、耗时,方便排查问题
posted @ 2026-08-01 02:49  漫漫长路</>  阅读(8)  评论(0)    收藏  举报