在 Java 后端项目里直接调用大模型,过去意味着手写 OkHttp 请求、拼接 JSON、处理流式 SSE,代码又臭又长。Spring AI 把这些都封装成统一的 ChatClient 抽象:换模型只改配置,不碰业务代码。本文用一套可运行的 Spring Boot 示例,带你从零接入 OpenAI、通义千问与本地 Ollama,并实现打字机式流式对话。
一、为什么 Java 后端需要 Spring AI
大模型能力正在下沉到业务系统:智能客服、工单摘要、SQL 生成、文档问答。如果为每个模型手写一个 HTTP 客户端,维护成本会随模型数量线性爆炸。Spring AI 的价值在于提供可移植的抽象层——ChatClient、EmbeddingClient、VectorStore 等接口,让模型成为可替换的”零件”。这与 Spring 一贯的”面向接口编程”理念一致,也方便复用你已有的 Spring Boot 3 升级经验,几乎零额外学习成本。
更现实的好处是:当某家模型涨价、限流或下线时,你只需改一行业务无关的配置文件,而不是翻遍 Service 层改请求拼装代码。这种解耦在 AI 供应商频繁变动的当下尤为关键。
对团队而言,统一的抽象也意味着更低的协作成本:新同学不需要先读通每家厂商的 OpenAPI 文档,只要会 ChatClient 一套 API,就能在所有模型上开发。测试时还能用内存实现的假客户端做单元验证,不烧真金白银的 token。这正是把”调模型”从一次性脚本变成可持续工程资产的转折点。
二、引入依赖与版本对齐
Spring AI 已独立于 Spring Boot 版本发布,需注意版本对齐。以 Maven 为例,引入 OpenAI 与通义千问(DashScope)两个 starter:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
<!-- 通义千问 / 本地 Ollama 引入对应 starter 即可 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-dashscope-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
版本矩阵变化较快,建议用 spring-ai-bom 统一管理版本,避免与 Spring Boot 3.x 的 Jackson、WebFlux 依赖冲突导致启动失败。
三、三行代码跑通第一个对话
注入 ChatClient.Builder,构建客户端后直接提问:
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/ask")
public String ask(@RequestParam String q) {
return chatClient.prompt().user(q).call().content();
}
}
call().content() 返回完整字符串;若需要结构化对象,可用 .call().entity(MyPojo.class) 让模型直接吐出 JSON 并反序列化,省去手写解析。这正是 Spring AI 比裸 HTTP 调用省心的核心。
四、一套代码接入多家大模型
Spring AI 的核心优势是”换模型不改代码”。不同厂商通过 spring.ai.*.api-key 与 base-url 切换,业务层完全无感知。常见配置对比如下:
| 模型 | starter | 关键配置 | 适用场景 |
|---|---|---|---|
| OpenAI | openai | api-key + model=gpt-4o | 通用能力强 |
| 通义千问 | dashscope | api-key + model=qwen-max | 中文、合规 |
| Ollama | ollama | base-url=http://localhost:11434 | 本地私有部署 |
以通义千问为例,application.yml 只需:
spring:
ai:
dashscope:
api-key: ${QWEN_API_KEY}
chat:
options:
model: qwen-max
想要零成本自托管、数据不出内网?参考我们的 Dify + Ollama 私有知识库 方案,把本地模型直接接进 Spring AI 的 Ollama starter,密钥与语料全部留在自己机器上。
如果需要在运行时动态切换模型(比如按用户等级路由到不同档位),可以注入多个 ChatClient.Builder 的 Bean,每个绑定不同 provider,再用一个工厂方法按条件返回对应客户端。业务代码全程只依赖 ChatClient 接口,切换供应商对它完全透明。注意把 API Key 放进环境变量或配置中心,切勿硬编码进仓库。
五、流式输出:打字机效果
生产环境几乎都要流式——用户不想干等三五秒才看到第一个字。把 call() 换成 stream(),返回 Flux<String>,配合 WebFlux 的 SSE 即可边生成边推送:
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String q) {
return chatClient.prompt()
.user(q)
.stream()
.content();
}
前端用 EventSource 接收即可,原理与 LLM 流式输出 SSE 实战一致。注意 WebFlux 与 MVC 返回类型不同:MVC 下可返回 StreamingResponseBody 或 ResponseBodyEmitter,否则响应会被缓冲成整段。本地调试可直接用 curl -N 观察分片推送是否正确。
六、给模型装上”工具”:Function Calling
模型本身不知道你的数据库今天有几单。通过 @Tool 或 FunctionToolCallback 把 Java 方法暴露给模型,它会在需要时自动调用:
@Bean
@Description("查询指定用户今日订单数")
public ToolCallback orderCountTool(OrderService svc) {
return FunctionToolCallback.builder("orderCount",
(OrderQuery q) -> svc.countToday(q.userId()))
.inputType(OrderQuery.class)
.build();
}
// 调用时把工具交给 ChatClient
chatClient.prompt().user("我今天的订单多少")
.tools(orderCountTool)
.call().content();
模型会先判断是否需要工具,自动生成符合 OrderQuery 入参 schema 的调用请求,执行 Java 方法后把结果回填再生成最终回答。这正是”AI Agent”能干活的基础能力。
七、生产级加固:超时、重试与降级
大模型 API 不稳定是常态,必须加护栏。用 Spring 的 Resilience4j 做超时与重试,并准备本地兜底话术:
ChatClient guarded = builder
.defaultOptions(OpenAiChatOptions.builder()
.withTimeout(Duration.ofSeconds(20)).build())
.build();
// 配合 @CircuitBreaker:连续失败时切换备用模型或返回模板话术
多模型路由也是刚需:用 LiteLLM 多模型网关 统一接入,Spring AI 只连一个端点,成本与可用性交给网关调度,避免被单一供应商绑死。
八、与现有 Spring Boot 应用集成
Spring AI 是普通 Spring Bean,可直接注入你已有的 Service 层。事务、缓存、异步都照常工作——但务必注意 大模型调用不要放进数据库事务,否则长耗时会拖垮连接池(详见 Spring 事务传播机制)。建议把 AI 调用放到 @Async 方法或独立线程池,主链路只负责接任务、收结果。
配置层面,强烈建议把 spring.ai.* 全量外置到 application.yml 并配合 @Profile 或配置中心:开发环境走便宜模型、生产环境走高能力模型、压测环境走本地 Ollama。这样同一份代码在四套环境切换无需改一行 Java。同时记得给 ChatClient 调用加上日志与耗时埋点,方便后续用 APM 定位慢模型与异常率。
九、常见坑与排查清单
问题1:No qualifying bean 'chatClient'
→ 漏了对应 starter 依赖,或配置里没给 api-key。
问题2:流式接口返回整段而非分片
→ MVC 下未设置 produces=TEXT_EVENT_STREAM_VALUE,
或被消息转换器缓冲,需返回 Flux / StreamingResponseBody。
问题3:频繁 429 限流
→ 开启 Resilience4j 重试 + 退避,或走 LiteLLM 网关做配额路由。
问题4:中文乱码 / 编码异常
→ 确认 application.yml 文件编码为 UTF-8,且 JVM -Dfile.encoding=UTF-8。
十、小结
Spring AI 让 Java 后端接入大模型变得像引入一个普通 Starter 一样简单:统一抽象、可换模型、原生流式、内置工具调用。把它接进你已有的 Spring Boot 体系,智能能力就能平滑下沉到业务里。下一步可以结合向量库做 RAG 检索增强,我们后续再展开。




