跳到主要内容Spring Boot WebClient 集成大模型 API:OpenAI、文心一言、通义千问 | 极客日志JavaAIjava
Spring Boot WebClient 集成大模型 API:OpenAI、文心一言、通义千问
综述由AI生成如何在 Spring Boot 项目中使用 WebClient 调用主流大模型 API。内容包括项目依赖配置、API 密钥管理、WebClient 基础设置。详细演示了 OpenAI、文心一言和通义千问的接口调用方法,涵盖请求构造、Token 获取及响应解析。此外,文章还提供了错误处理机制、最佳实践建议(如超时设置、重试策略、流式输出)以及完整的项目结构示例,帮助开发者构建高性能、生产级的 AI 集成应用。
数字游民31 浏览 Spring Boot 作为现代 Java 开发的首选框架,其内置的 WebClient 是调用 REST API 的高效方式。本文介绍如何使用它调用主流大模型 API。
一、为什么选择 WebClient?
| 特性 | RestTemplate | WebClient |
| 同步/异步 | 同步(阻塞) | 异步(非阻塞,响应式) |
| 线程模型 | 每个请求占用一个线程 | 事件驱动,高并发 |
| 流式处理 | 不支持 | 支持响应流(Streaming) |
| 错误处理 | 复杂 | 链式调用,简洁 |
| Spring 生态 | 旧版(Spring 5 开始弃用) | Spring 5+ 推荐 |
推荐 WebClient:它更符合现代微服务架构的需求,能显著提升系统吞吐量。
二、项目准备
1. 创建 Spring Boot 项目
使用 Spring Initializr 创建项目,选择以下依赖:
- Spring Webflux(自动包含
WebClient)
- Lombok(可选,简化 DTO 代码)
2. pom.xml 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok
lombok
provided
</groupId>
<artifactId>
</artifactId>
<scope>
</scope>
</dependency>
</dependencies>
3. 读取 API 密钥(推荐使用环境变量)
在 application.yml 中配置密钥(切勿硬编码):
api:
openai:
key: ${OPENAI_API_KEY}
wenxin:
api-key: ${WENXIN_API_KEY}
secret-key: ${WENXIN_SECRET_KEY}
tongyi:
key: ${TONGYI_API_KEY}
@Service
public class ApiConfig {
@Value("${api.openai.key}")
private String openaiApiKey;
@Value("${api.wenxin.api-key}")
private String wenxinApiKey;
@Value("${api.wenxin.secret-key}")
private String wenxinSecretKey;
@Value("${api.tongyi.key}")
private String tongyiApiKey;
}
三、WebClient 基础配置
创建一个 WebClientConfig 类,封装通用配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.responseTimeout(java.time.Duration.ofSeconds(30));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
}
提示:生产环境中应配置重试策略(如 Retry)、日志跟踪等。
四、调用 OpenAI API
1. OpenAI API 简介
- 端点:
https://api.openai.com/v1/chat/completions
- 必需头:
Authorization: Bearer <API_KEY>
- 请求体:JSON 格式,包含
model、messages 等字段。
- 官方文档:OpenAI API Reference
2. DTO 定义
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
class OpenAiRequest {
private String model;
private List<Message> messages;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Message {
private String role;
private String content;
}
@Data
class OpenAiResponse {
private List<Choice> choices;
@Data
static class Choice {
private Message message;
}
}
3. 服务实现
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class OpenAiService {
private final WebClient webClient;
private final String apiKey;
public OpenAiService(WebClient.Builder webClientBuilder, @Value("${api.openai.key}") String apiKey) {
this.webClient = webClientBuilder
.baseUrl("https://api.openai.com/v1")
.defaultHeader("Authorization", "Bearer " + apiKey)
.build();
this.apiKey = apiKey;
}
public Mono<String> chat(String prompt) {
OpenAiRequest request = new OpenAiRequest(
"gpt-3.5-turbo",
List.of(new Message("user", prompt))
);
return webClient.post()
.uri("/chat/completions")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.retrieve()
.bodyToMono(OpenAiResponse.class)
.map(response -> response.getChoices().get(0).getMessage().getContent());
}
}
4. Controller 使用
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/ai")
public class AiController {
private final OpenAiService openAiService;
public AiController(OpenAiService openAiService) {
this.openAiService = openAiService;
}
@GetMapping("/ask")
public Mono<String> askOpenAi(@RequestParam String question) {
return openAiService.chat(question);
}
}
测试:发送 GET 请求 http://localhost:8080/ai/ask?question=你好,将返回 GPT 的回答。
五、调用文心一言 API
1. 文心一言 API 简介
- 获取访问令牌(Access Token):
- 首先需调用 OAuth 2.0 获取
access_token。
- 端点:
https://aip.baidubce.com/oauth/2.0/token
- 参数:
grant_type=client_credentials&client_id=<API_KEY>&client_secret=<SECRET_KEY>
- 聊天接口:
- 端点:
https://aip.baidubce.com/rpc/2.0/ai/custom/v1/wenxinworkshop/chat/completions?access_token=<TOKEN>
- 请求体格式类似 OpenAI,但字段名不同。
注意:文心一言需在百度智能云控制台创建应用并获取密钥。
2. DTO 定义
@Data
class WenxinTokenResponse {
private String access_token;
private String expires_in;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class WenxinRequest {
private String model = "eb-instant";
private List<Message> messages;
}
@Data
class WenxinResponse {
private List<Choice> result;
@Data
static class Choice {
private String content;
}
}
3. 服务实现
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class WenxinService {
private final WebClient webClient;
private final String apiKey;
private final String secretKey;
public WenxinService(WebClient.Builder webClientBuilder, @Value("${api.wenxin.api-key}") String apiKey, @Value("${api.wenxin.secret-key}") String secretKey) {
this.webClient = webClientBuilder.build();
this.apiKey = apiKey;
this.secretKey = secretKey;
}
private Mono<String> getAccessToken() {
return webClient.post()
.uri("https://aip.baidubce.com/oauth/2.0/token")
.queryParam("grant_type", "client_credentials")
.queryParam("client_id", apiKey)
.queryParam("client_secret", secretKey)
.retrieve()
.bodyToMono(WenxinTokenResponse.class)
.map(WenxinTokenResponse::getAccess_token);
}
public Mono<String> chat(String prompt) {
return getAccessToken().flatMap(accessToken -> {
WenxinRequest request = new WenxinRequest(List.of(new Message("user", prompt)));
return webClient.post()
.uri("https://aip.baidubce.com/rpc/2.0/ai/custom/v1/wenxinworkshop/chat/completions")
.queryParam("access_token", accessToken)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.retrieve()
.bodyToMono(WenxinResponse.class)
.map(resp -> resp.getResult().get(0).getContent());
});
}
}
4. Controller 使用
@RestController
@RequestMapping("/ai")
public class AiController {
private final WenxinService wenxinService;
public AiController(WenxinService wenxinService) {
this.wenxinService = wenxinService;
}
@GetMapping("/wenxin")
public Mono<String> askWenxin(@RequestParam String question) {
return wenxinService.chat(question);
}
}
六、调用通义千问 API
1. 通义千问 API 简介
- 端点:
https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation
- 必需头:
Authorization: Bearer <API_KEY>
- 模型:
dall-e-xl(图像)或 qwen-plus(文本)
- 官方文档:通义千问 API 文档
注意:通义千问由阿里云提供,需在阿里云控制台获取 API 密钥。
2. DTO 定义
import java.util.ArrayList;
@Data
@NoArgsConstructor
@AllArgsConstructor
class TongyiRequest {
private String model = "qwen-long";
private List<Message> input = new ArrayList<>();
private Parameters parameters = new Parameters();
@Data
@NoArgsConstructor
@AllArgsConstructor
static class Parameters {
private float temperature = 0.7f;
private int max_tokens = 800;
}
}
@Data
class TongyiResponse {
private Output output;
@Data
static class Output {
private List<Text> text;
@Data
static class Text {
private String content;
}
}
}
3. 服务实现
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class TongyiService {
private final WebClient webClient;
private final String apiKey;
public TongyiService(WebClient.Builder webClientBuilder, @Value("${api.tongyi.key}") String apiKey) {
this.webClient = webClientBuilder
.baseUrl("https://dashscope.aliyuncs.com")
.defaultHeader("Authorization", "Bearer " + apiKey)
.defaultHeader("Content-Type", MediaType.APPLICATION_JSON)
.build();
this.apiKey = apiKey;
}
public Mono<String> chat(String prompt) {
TongyiRequest request = new TongyiRequest();
request.getInput().add(new Message("user", prompt));
return webClient.post()
.uri("/api/v1/services/aigc/text-generation/generation")
.bodyValue(request)
.retrieve()
.bodyToMono(TongyiResponse.class)
.map(resp -> resp.getOutput().getText().get(0).getContent());
}
}
4. Controller 使用
@RestController
@RequestMapping("/ai")
public class AiController {
private final TongyiService tongyiService;
public AiController(TongyiService tongyiService) {
this.tongyiService = tongyiService;
}
@GetMapping("/tongyi")
public Mono<String> askTongyi(@RequestParam String question) {
return tongyiService.chat(question);
}
}
七、错误处理与最佳实践
1. 优雅处理 HTTP 错误
使用 onStatus() 捕获 4xx/5xx 错误:
public Mono<String> chat(String prompt) {
return webClient.post()
.uri("/chat/completions")
.bodyValue(request)
.retrieve()
.onStatus(httpStatus -> httpStatus.value() >= 400, clientResponse -> {
return clientResponse.bodyToMono(String.class)
.flatMap(errorBody -> Mono.error(new RuntimeException(
"API 调用失败:" + httpStatus.getReasonPhrase() + ", " + errorBody
)));
})
.bodyToMono(OpenAiResponse.class)
.map(...);
}
2. 重要最佳实践
| 实践 | 原因 |
| 密钥隔离 | 使用环境变量、Vault 或 KMS,绝不硬编码在代码中 |
| 超时设置 | 避免请求长时间挂起(如 `.responseTimeout(Duration.ofSeconds(30))`) |
| 重试机制 | 对 5xx 错误使用 `Retry`(如 `Retry.backoff(3, Duration.ofSeconds(1))`) |
| 流式处理 | 大模型响应可能很长,支持流式输出(如 OpenAI 的 `stream=true`) |
| 日志与监控 | 记录请求参数、响应码、耗时,方便排查问题 |
| 令牌刷新 | 文心一言的 `access_token` 有有效期(24 小时),需定期刷新 |
| 响应式编程 | 在 Reactive 项目中,避免使用 `.block()`,直接返回 `Mono/Flux` |
3. 流式输出示例(OpenAI)
public Flux<String> streamChat(String prompt) {
OpenAiRequest request = new OpenAiRequest("gpt-3.5-turbo", List.of(new Message("user", prompt)));
request.setStream(true);
return webClient.post()
.uri("/chat/completions")
.bodyValue(request)
.accept(MediaType.TEXT_EVENT_STREAM)
.exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(String.class))
.filter(chunk -> !chunk.trim().isEmpty())
.map(chunk -> chunk.split("data: ")[1])
.filter(data -> !data.equals("[\u200b]"))
.map(data -> { });
}
八、完整项目结构
src
├── main
│ ├── java
│ │ └── com.example.demo
│ │ ├── DemoApplication.java
│ │ ├── config
│ │ │ └── WebClientConfig.java
│ │ ├── controller
│ │ │ └── AiController.java
│ │ ├── dto
│ │ │ ├── OpenAiRequest.java
│ │ │ ├── WenxinRequest.java
│ │ │ └── TongyiRequest.java
│ │ ├── service
│ │ │ ├── OpenAiService.java
│ │ │ ├── WenxinService.java
│ │ │ └── TongyiService.java
│ │ └── ApiConfig.java
│ └── resources
│ └── application.yml
九、总结
- 如何配置 Spring Boot 的
WebClient 作为高性能 HTTP 客户端。
- 三大主流大模型 API 的调用流程:
- OpenAI:直接传递
Bearer Token。
- 文心一言:先获取
access_token,再带参调用。
- 通义千问:使用
Bearer 方式传递 API 密钥。
- 响应式编程实践:使用
Mono/Flux 处理异步调用,避免线程阻塞。
- 生产级考量:错误处理、密钥管理、超时重试、流式输出等。
- 集成 Spring AI(官方支持的 AI 框架),简化大模型调用。
- 尝试 流式输出,提升用户体验(如实时显示模型思考过程)。
- 使用 缓存 存储重复请求的结果,降低 API 调用成本。
现在,你可以将大模型能力集成到自己的 Spring Boot 应用中!
重要提醒:
大模型 API 通常需要付费(按 token 计费),请理性使用,并遵守各平台的使用政策。实际项目中请仔细阅读官方文档获取最新接口规范。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online