一、💡 一句话理解
第 4 天核心结论
ChatClient是你在 Spring Boot 业务代码中调用大模型的客户端。今天只要学会:Spring Boot 自动提供ChatClient.Builder,你把它build()成ChatClient,再用prompt(...).call().content()得到模型回答。
本篇对应 0-学习路线 的第 4 天:Spring AI ChatClient。
今天完成后,你应该能做到:启动现有 Spring Boot 项目,访问一个 Java 接口,并看到模型返回的普通文本。
浏览器或 HTTP 请求
↓
Controller 使用 ChatClient 提问
↓
模型服务生成回答
↓
Controller 返回字符串二、🧭 理论:它是什么
2.1 ChatClient 是什么
ChatClient 是 Spring AI 提供的高级调用客户端。它帮业务代码完成“组织本次提问 → 调用底层模型 → 取出回答”的过程。
可以把它理解成 Java 后端和大模型之间的一个操作面板:你给它问题,它负责把问题交给已经配置好的模型服务,再把回答交还给你的代码。
今天只关注普通文本问答,不学习模型角色、流式输出、记忆或工具调用。
2.2 ChatClient 和 ChatModel 的关系
| 组件 | 现在先这样理解 |
|---|---|
ChatModel | 底层模型调用能力,负责和 DeepSeek、OpenAI 等服务通信。 |
ChatClient | 业务代码直接使用的客户端,调用方式更简洁。 |
ChatClient.Builder | 用来创建 ChatClient 的构建器。 |
在你的项目中,模型地址、模型名和 API Key 已经在 application.properties 与 IDEA 运行配置中准备好。Spring Boot 根据这些配置创建底层 ChatModel,再自动提供 ChatClient.Builder。
所以今天的业务代码不需要再次写 API Key、模型名或 HTTP 请求地址。
三、⚙️ 理论:它是怎么工作的
3.1 ChatClient 从哪里来
Spring Boot 启动后,会把自动配置好的 ChatClient.Builder 放进 Spring 容器。Controller 的构造方法请求这个对象,Spring 就会自动注入它。
private final ChatClient chatClient;
public AiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}这四行代码的过程是:
Spring Boot 自动配置模型
↓
创建 ChatClient.Builder
↓
注入到 AiController 构造方法
↓
builder.build() 得到可使用的 chatClientchatClient 是 Controller 的成员变量,后续每个接口方法都可以使用它。
3.1.1 构造方法这一段逐行是什么意思
public AiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}先看结论:这不是你手动执行的代码。Spring Boot 启动时要创建 AiController 对象,发现它的构造方法需要一个 ChatClient.Builder,就从 Spring 容器中取出已经准备好的 Builder 传进来。
| 代码 | 含义 |
|---|---|
public AiController(...) | AiController 的构造方法。创建 Controller 对象时会执行一次。 |
ChatClient.Builder | 参数的类型。这个类型本身提供了 build() 方法。 |
builder | 参数变量名,只是当前方法里对 Builder 对象的称呼;也可以命名为 chatClientBuilder。 |
builder.build() | 调用 Builder 的创建方法,得到一个真正可调用模型的 ChatClient 对象。 |
this.chatClient | 当前 AiController 对象中的成员变量。this 的意思是“当前这个 Controller 自己”。 |
= | 把刚创建的 ChatClient 保存到成员变量中,供 test1() 等接口方法后续使用。 |
为什么 builder 能调用 build()?因为它的类型是 ChatClient.Builder,而 Spring AI 在这个类型中定义了 build() 方法。Java 判断“能不能调用某个方法”,看的是变量的类型,不是变量名字。
下面两个写法完全一样,只是参数变量名不同:
public AiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public AiController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}Spring Boot 创建 Controller 的完整过程是:
Spring Boot 启动
→ 读取 Spring AI Starter、模型地址、模型名和 API Key 配置
→ 自动创建 ChatModel 和 ChatClient.Builder
→ 扫描到 @RestController 的 AiController
→ 发现构造方法需要 ChatClient.Builder
→ 从 Spring 容器取出 Builder,作为 builder 参数传入
→ 执行 builder.build()
→ 得到并保存 chatClient这叫“构造方法注入”:对象一创建,就把它依赖的对象传给它。这样 AiController 不用自己 new ChatClient,也不用自己处理模型配置。
3.2 最小调用链
最简单的普通问答是:
String answer = chatClient
.prompt("Spring Boot 和 Spring AI 有什么关系?")
.call()
.content();| 调用 | 当前作用 |
|---|---|
prompt("...") | 放入本次要问模型的文字。第 5 天会专门学习 Prompt、System Prompt 和模板。 |
call() | 声明使用普通的一次性调用方式。 |
content() | 真正取出模型生成的文本;结果类型是 String。 |
调用流程是:
问题文本
→ ChatClient 组织本次调用
→ ChatModel 请求已配置的模型服务
→ 模型生成回答
→ content() 取出回答字符串四、🚀 实践:从准备到验证
4.1 前置准备
本篇不重复第 1 天的项目创建和第 3 天的模型 API 配置。开始今天的代码前,只确认下面三件事已经完成。
4.1.1 已有 Spring Boot 项目和 OpenAI 兼容模型配置
你的当前项目已经使用 DeepSeek 的 OpenAI 兼容接口,配置应类似:
spring.ai.openai.api-key=${DEEPSEEK_API_KEY}
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.chat.model=deepseek-v4-flashDEEPSEEK_API_KEY 的真实值放在 IntelliJ IDEA 的 AiApplication 运行配置的环境变量中,不写进这个文件。
如果还不理解 API Key、模型名和 base-url,先回看 2.大模型 API、请求参数与响应结构。
4.1.2 已添加 Spring AI OpenAI Starter
pom.xml 中需要有:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>它的作用是让 Spring Boot 能够创建底层模型和 ChatClient.Builder。
4.1.3 启动后端项目
打开 AiApplication,点击 IntelliJ IDEA 的绿色运行按钮。成功后保持项目运行,并确认服务监听 8080 端口。
今天用的是普通同步调用,不需要先学习 SSE,也不需要启动前端项目。
4.2 可以拿来干什么
4.2.1 做一次普通文本问答
用途:后端向模型发送固定问题,拿到一段普通字符串回答。这是后续聊天接口、Prompt、结构化输出、流式输出等能力的共同起点。
前置:已完成 4.1,并且已经通过构造方法创建了 chatClient。
String answer = chatClient
.prompt("Spring Boot 和 Spring AI 有什么关系?")
.call()
.content();输入是问题字符串;输出是模型生成的 String。今天先把这个字符串直接返回给 HTTP 调用方,下一步再学习怎样控制问题格式和回答风格。
4.3 完整实践:第一个 ChatClient 接口
目标:在当前项目中新增一个 /test1 接口。访问它后,后端调用模型,并把模型回答直接返回。
4.3.1 在 Controller 中写入代码
文件位置:
src/main/java/com/afyke/ai/controller/AiController.java如果文件已经存在,只需要确认其中有下面的 chatClient 字段、构造方法和 test1 方法;不要重复创建两个同名 Controller。
package com.afyke.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AiController {
private final ChatClient chatClient;
public AiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/test1")
public String test1() {
return chatClient
.prompt("Spring Boot 和 Spring AI 有什么关系?")
.call()
.content();
}
}输入:代码中固定的问题“Spring Boot 和 Spring AI 有什么关系?”。
处理:test1() 调用 chatClient,模型生成文本,content() 取回文本。
输出:接口返回模型生成的一段普通文字。
4.3.2 用 HTTP Client 验证
在后端项目根目录的 request.http 中添加:
### 第 4 天:ChatClient 普通文本调用
GET http://localhost:8080/test1启动 AiApplication 后,点击请求左侧的绿色运行按钮。
预期结果:HTTP 状态为 200,响应正文是一段模型对 Spring Boot 和 Spring AI 关系的回答。
至此,今天的链路已经跑通:
GET /test1
→ AiController.test1()
→ chatClient.prompt(...).call().content()
→ DeepSeek
→ 返回文本五、📌 总结
- 本篇对应学习路线第 4 天,只学习
ChatClient的普通文本调用。 - Spring Boot 自动提供
ChatClient.Builder,构造方法注入后调用build()得到ChatClient。 prompt(...).call().content()是最小调用链,结果是String。- 今天的验收是
GET /test1能返回模型回答。 - 第 5 天再学习 Prompt、System Prompt 和 Prompt 模板。
- 第 6 天再学习结构化输出;第 7 天学习 SSE;第 8 天学习会话 ID、聊天记录和 Redis。
- RAG、Tool Calling 和 Agent 工作流属于后续阶段,不混入本篇。
记忆句:
ChatClient = 注入 Builder → build() → prompt() → call() → content()
官方资料: