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 内置
RestClient 和 HTTP 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.comjava
运行
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 的设计 |
九、最佳实践总结
-
选型原则
- 固定接口、标准化调用 → 优先用 HTTP Service,代码最少、维护性最强
- 动态 URL、复杂请求、需要精细控制 → 用 RestClient
- 旧项目兼容可保留 RestTemplate,新项目直接上 RestClient
-
配置规范
- 统一抽取 baseUrl、超时、公共头到配置类,不要硬编码在业务代码里
- 必须设置连接超时和读取超时,防止外部接口故障拖垮自身服务
- 不同外部服务分组配置,各自独立 Bean,避免混淆
-
异常与容错
- 对 4xx、5xx 状态码做分类处理,不要全吞异常
- 生产环境建议配合重试、熔断组件(如 Resilience4j)
- 外部调用必须打日志,记录入参、出参、耗时,方便排查问题

浙公网安备 33010602011771号