保姆级|Spring AI 2.0 第一个 Demo:Spring Boot 4 接入大模型(含国内可用方案 + 完整 pom)
本文将解决:5 分钟内,用 Spring AI 2.0 的 ChatClient 把第一个大模型回答打印到控制台,并给出 OpenAI / DeepSeek / 通义 / Ollama 四种国内可用接入方案。
你将学到:
- 为什么 Java 接入大模型首选 Spring AI,而不是手搓 HTTP
- Spring AI 2.0 的依赖坐标与版本管理(spring-ai-bom)
- 最小 application.yml 配置(base-url / api-key)
- 两行代码完成第一次 ChatClient.call()
- 三种国内可用方案:OpenAI 兼容协议接 DeepSeek/通义、本地 Ollama 零成本
- 5 个新手必踩的坑(base-url、代理、超时、Key、版本冲突)
一、为什么 Java 接入大模型要选 Spring AI
很多 Java 开发者第一次接大模型,会用 RestTemplate / WebClient 手搓 OpenAI 的 HTTP 接口:自己拼 JSON、自己解析响应、自己处理流式 SSE。能跑,但很快会撞墙:换模型要重写、流式输出要自己管背压、工具调用要自己实现多轮循环、RAG 要自己接向量库。
Spring AI 的价值,就是把这套东西变成 Spring 生态里的原生抽象:像用 JdbcTemplate 一样用 ChatClient,像换数据库一样换模型。它由 Spring 官方团队维护,2.0 已经是生产可用的 GA 版本。
一句话定位:Spring AI 是 Spring 官方对"Java 调用大模型"这件事的标准化封装,不是又一个通用 Agent 框架,也不和 LangChain4j 抢同一个生态位——它深度融入 Spring Boot 的自动装配、可观测、依赖管理。
二、版本标注(重要,先看清楚)
本文基于以下版本,2.0 相对 1.x 有 breaking 变化,不要照抄 1.x 的老教程:
| Spring AI | 2.0.1 | BOM 统一锁版本 |
| Spring Boot | 4.1.x | 2.0 要求 Boot 4.0/4.1,不是 Boot 3 |
| JDK | 21+ | 2.0 最低要求 JDK 21,不是 JDK 17 |
⚠️ 如果你还在用 Boot 3.x + JDK 17 + Spring AI 1.x,请看本系列第 4 篇《1.x→2.0 迁移指南》。
三、环境准备
- JDK 21(java -version 确认)
- Maven 3.9+
- 一个模型服务的 API Key(OpenAI 或 DeepSeek 任选,下文给出国内可用方案)
不需要 GPU、不需要装向量库、不需要任何额外中间件——入门就是这么轻。
四、依赖与完整 pom.xml
Spring AI 的版本管理靠 BOM(Bill of Materials):引入 spring-ai-bom 后,下面所有 spring-ai-starter-* 依赖都不用再写版本号,避免版本冲突。
创建 Maven 项目,pom.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>
<!– Spring Boot 4 父工程,统一依赖与插件版本 –>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai-demo</name>
<description>Spring AI 2.0 第一个 Demo</description>
<properties>
<java.version>21</java.version>
<spring-ai.version>2.0.1</spring-ai.version>
</properties>
<!– 关键:spring-ai-bom 统一锁 Spring AI 所有模块版本 –>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!– Web 依赖:让 AI 通过 HTTP 接口对外提供服务 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!– OpenAI 模型 starter(版本由 BOM 管理) –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
两个关键点:
五、最小 application.yml 配置
src/main/resources/application.yml:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY} # 从环境变量读取,别把 Key 硬编码进代码
base-url: https://api.openai.com # 默认就是它,接国产模型时改成对应地址
chat:
options:
model: gpt–4o–mini # 模型名;换成你的模型即可
temperature: 0.7
说明:2.0 对配置属性做了扁平化调整,chat.options.* 这层在部分 2.0.x 版本里已改名。本文代码尽量把 model/temperature 放到 Java 侧的 ChatOptions.builder() 里,从根上规避属性名漂移,配置里只留最稳的 api-key 和 base-url。这也是工程上的推荐做法——参数跟代码走,配置跟环境走。
六、第一次调用:两行代码跑通
最直接的跑通方式:写一个 CommandLineRunner,应用启动时打印一次 AI 回复,不用开浏览器、不用写 Controller。
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringAiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiDemoApplication.class, args);
}
@Bean
CommandLineRunner runner(ChatClient.Builder builder) {
return args -> {
ChatClient chatClient = builder.build();
String reply = chatClient.prompt()
.user("用一句话介绍 Spring AI")
.call()
.content();
System.out.println("AI 回复:" + reply);
};
}
}
启动应用(mvn spring-boot:run),控制台会打印出 AI 回复。到此你已经跑通了 Spring AI 2.0。
核心就一个链式调用:builder.build() 拿到 ChatClient,.prompt().user("…").call().content() 发起请求并取回字符串。
七、进阶:暴露成 HTTP 接口
实际项目里 AI 能力要通过接口对外提供,加一个 Controller:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatController {
private final ChatClient chatClient;
// 直接注入 ChatClient.Builder,由框架装配
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam(defaultValue = "你好,介绍一下你自己") String q) {
return chatClient.prompt()
.user(q)
.call()
.content();
}
}
启动后访问:http://localhost:8080/chat?q=写一首关于Java的诗,即可得到回复。
八、国内可用方案(三种)
OpenAI 的 Key 很多人没有,这里给出三种国内可用的替代方案。
方案 A:DeepSeek(OpenAI 兼容协议,改 base-url 即可)
DeepSeek 提供了 OpenAI 兼容接口,不用换 starter,只改配置:
spring:
ai:
openai:
api-key: ${DEEPSEEK_API_KEY}
base-url: https://api.deepseek.com # 也可写 https://api.deepseek.com/v1
chat:
options:
model: deepseek–chat # DeepSeek 的对话模型
代码完全不用改——这就是 Spring AI 抽象的价值(第 3 篇会展开讲)。
方案 B:通义千问(阿里云,走兼容协议)
阿里云百炼的 DashScope 也提供 OpenAI 兼容端点:
spring:
ai:
openai:
api-key: ${DASHSCOPE_API_KEY}
base-url: https://dashscope.aliyuncs.com/compatible–mode/v1
chat:
options:
model: qwen–plus
方案 C:本地 Ollama(零成本、零 Key、数据不出本地)
Ollama 让你在本地跑开源模型,完全免费、无需 Key、数据私有。先装 Ollama 并拉一个模型:
# 安装后拉取一个模型(以 Qwen2.5 为例)
ollama pull qwen2.5
然后换 starter 和配置:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
spring:
ai:
ollama:
base-url: http://localhost:11434 # Ollama 默认本地端口
chat:
options:
model: qwen2.5 # 必须是已 ollama pull 的模型名
三种方案对比:
| OpenAI | spring-ai-starter-model-openai | https://api.openai.com | 按量付费 | 是 | 效果最强 |
| DeepSeek | spring-ai-starter-model-openai | https://api.deepseek.com | 便宜 | 是 | 性价比高 |
| 通义千问 | spring-ai-starter-model-openai | https://dashscope.aliyuncs.com/compatible-mode/v1 | 按量 | 是 | 国内合规 |
| Ollama | spring-ai-starter-model-ollama | http://localhost:11434 | 免费 | 否 | 本地/私有化 |
九、原理图解:一次调用的完整链路
OpenAI/DeepSeek/OllamaChatModelChatClientChatController浏览器/控制台OpenAI/DeepSeek/OllamaChatModelChatClientChatController浏览器/控制台#mermaid-svg-Kn4A7222KfLh6EII{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Kn4A7222KfLh6EII .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Kn4A7222KfLh6EII .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Kn4A7222KfLh6EII .error-icon{fill:#552222;}#mermaid-svg-Kn4A7222KfLh6EII .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Kn4A7222KfLh6EII .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Kn4A7222KfLh6EII .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Kn4A7222KfLh6EII .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Kn4A7222KfLh6EII .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Kn4A7222KfLh6EII .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Kn4A7222KfLh6EII .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Kn4A7222KfLh6EII .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Kn4A7222KfLh6EII .marker.cross{stroke:#333333;}#mermaid-svg-Kn4A7222KfLh6EII svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Kn4A7222KfLh6EII p{margin:0;}#mermaid-svg-Kn4A7222KfLh6EII .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Kn4A7222KfLh6EII text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Kn4A7222KfLh6EII .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Kn4A7222KfLh6EII .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Kn4A7222KfLh6EII .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Kn4A7222KfLh6EII .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Kn4A7222KfLh6EII #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Kn4A7222KfLh6EII .sequenceNumber{fill:white;}#mermaid-svg-Kn4A7222KfLh6EII #sequencenumber{fill:#333;}#mermaid-svg-Kn4A7222KfLh6EII #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Kn4A7222KfLh6EII .messageText{fill:#333;stroke:none;}#mermaid-svg-Kn4A7222KfLh6EII .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Kn4A7222KfLh6EII .labelText,#mermaid-svg-Kn4A7222KfLh6EII .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Kn4A7222KfLh6EII .loopText,#mermaid-svg-Kn4A7222KfLh6EII .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Kn4A7222KfLh6EII .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Kn4A7222KfLh6EII .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Kn4A7222KfLh6EII .noteText,#mermaid-svg-Kn4A7222KfLh6EII .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Kn4A7222KfLh6EII .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Kn4A7222KfLh6EII .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Kn4A7222KfLh6EII .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Kn4A7222KfLh6EII .actorPopupMenu{position:absolute;}#mermaid-svg-Kn4A7222KfLh6EII .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Kn4A7222KfLh6EII .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Kn4A7222KfLh6EII .actor-man circle,#mermaid-svg-Kn4A7222KfLh6EII line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Kn4A7222KfLh6EII :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}GET /chat?q=你好prompt().user(q).call()组装 Prompt(含角色/参数)HTTP 请求(携带 api-key)模型生成的回复ChatResponsecontent() 取字符串返回回复
要点:你的业务代码只和 ChatClient 打交道,ChatClient 背后是 ChatModel,ChatModel 再去调真正的模型服务。换模型(DeepSeek/通义/Ollama)只是换 ChatModel 的实现,业务代码零改动。
十、踩坑 FAQ(新手必看,都是真实报错)
坑 1:启动报 NoSuchMethodError / 类找不到
现象:一启动就报 java.lang.NoSuchMethodError 或 ClassNotFoundException。
原因:忘了引入 spring-ai-bom,导致各 starter 版本互相冲突;或把 starter 写成了旧名 spring-ai-openai-spring-boot-starter。
解决:确认 pom.xml 里有 dependencyManagement 引入 spring-ai-bom,starter 用 spring-ai-starter-model-openai。
坑 2:报 apiKey must not be null
现象:启动或调用时抛异常,提示 api key 为空。
原因:api-key 没配,或环境变量 OPENAI_API_KEY 没设。
解决:确认 application.yml 里 api-key: ${OPENAI_API_KEY},并在启动前 export OPENAI_API_KEY=sk-xxx。
坑 3:请求返回 404
现象:能启动,但调用模型报 404。
原因:base-url 写错了路径——常见是少了 /v1 或多写了一段。
解决:核对 base-url。DeepSeek 用 https://api.deepseek.com(或其 /v1);通义兼容端点必须带 /compatible-mode/v1。
坑 4:Connection refused 或 Read timed out
现象:连接被拒或读超时。
原因:公司/校园网络有代理拦截;或 Ollama 没启动。
解决:走代理时给 JVM 加代理参数 -Dhttp.proxyHost=… -Dhttp.proxyPort=…;用 Ollama 前先 ollama serve 确保服务在 11434 端口。
坑 5:Boot 3 上跑 Spring AI 2.0 起不来
现象:把 spring-ai 版本改成 2.0,但 Boot 还是 3.x,启动报兼容错。
原因:Spring AI 2.0 只支持 Boot 4.0/4.1,和 Boot 3 不兼容。
解决:要么 Boot 升到 4.1,要么 Spring AI 降到 1.1.x。两者版本必须匹配(详见第 4 篇迁移指南)。
十一、小结与合集导航
这一篇你完成了 Spring AI 2.0 的"从 0 到 1":BOM 版本管理、最小配置、ChatClient 首次调用、三种国内可用方案、五个新手坑。
下一步建议按顺序读:
- 下一篇:《ChatClient Fluent API 全拆解》——把 .prompt()/call()/stream() 和消息角色、参数调优彻底讲透
- 收藏备用:本合集「Spring AI 深入浅出」总目录(文末置顶评论有链接)
- 源码仓库:本篇完整可运行代码见文末置顶评论的 GitHub 仓库
十二、互动钩子
投票:你准备用哪个模型接入?
- OpenAI
- DeepSeek
- 通义千问
- 本地 Ollama
- 其他
评论区晒出你的第一个跑通截图,我会逐条答疑;源码与合集导航已置顶评论,欢迎自取。





