适合人群:Spring Boot 会一点,想接大模型,但被"四种接入方式"卡在第一步的同学。
上一篇把 12 个名词理清了,但那只是纸面功夫。真正打开 IDEA,第一个问题立刻变成:
这四种写法,我到底敲哪种?
于是我把同一句话------「你是谁?」------用四种方式各写了一遍。代码全部贴出来,谁能用、谁坑多,跑完就清楚了。
一、先把结论放这儿

四种方式的横向对比
如果你只想记住一句话:
先用官方 SDK 打通,确认 Key 和网络没问题;真正写业务时切 Spring AI。
理由很简单:SDK 是官方维护的,出错信息最直接,能帮你把「Key 对不对、模型名对不对、网络通不通」这三个基础问题一次排干净。等问题都排除了,再上框架,才不会把「环境问题」误判成「框架问题」。
HTTP 直连不是给日常开发用的,它属于「SDK 不支持这门语言」或「我要看清协议到底发了什么」的场景。
二、统一测试条件(很重要)
四种方式对比,最怕变量不一致。我固定了这几个条件:
| 项 | 值 |
|---|---|
| 模型 | qwen-plus(同一家的同一个模型) |
| 项目 | Spring Boot 3.4.4 + JDK 21 |
| 提示词 | 都是「你是谁?」 |
| Key | 同一个,走环境变量 |
为什么强调"同一个模型":不同厂商返回结构完全不同。拿 A 家的 SDK 和 B 家的框架比"代码行数",比的其实是厂商差异,不是框架差异。
Key 别写死在代码里:
yaml
# application.yml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
powershell
# Windows 设置环境变量(设完要重开 IDEA)
setx AI_DASHSCOPE_API_KEY "sk-xxxxxx"
顺手一句:Key 提交到 Git 等于公开。公开仓库有爬虫专门扫
sk-开头的字符串,几分钟就会被抓走。
三、方式一:官方 SDK(最直接)
xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.19.1</version>
</dependency>
java
public class SdkAiInvoke {
public static GenerationResult callWithMessage() throws Exception {
Generation gen = new Generation();
Message systemMsg = Message.builder()
.role(Role.SYSTEM.getValue())
.content("You are a helpful assistant.")
.build();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content("你是谁?")
.build();
GenerationParam param = GenerationParam.builder()
.apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
.model("qwen-plus")
.messages(Arrays.asList(systemMsg, userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
return gen.call(param);
}
}
跑通之后,控制台会打印类似这样的结构:
ini
usage: input_tokens=22, output_tokens=75
choices[0].message.content: 我是通义千问......
两个细节,教程通常一笔带过:
① .resultFormat(MESSAGE) 这一行不能省。 不设的话返回的是扁平文本,取值方式完全不同。你按 getChoices().get(0).getMessage().getContent() 去取会拿到 null,然后开始怀疑人生。
② model 传的是模型名,不是应用 ID。 换成 qwen-max、qwen-turbo 只改这一行,这是 SDK 最舒服的地方。
它的问题也很明显:绑死一家。哪天要换 DeepSeek,代码得重写一遍;要做 RAG、工具调用,这些高级能力它基本不管,全得自己搭。
四、方式二:HTTP 直连(理解协议的最佳方式)
bash
curl -X POST https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation \
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"model": "qwen-plus",
"input": { "messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "你是谁?" }
]},
"parameters": { "result_format": "message" }
}'
Java 里用 Hutool 拼一遍,核心就三行:
java
HttpResponse response = HttpRequest.post(url)
.addHeaders(headers)
.body(requestBody.toString())
.execute();
这里有个非常容易搞混的点,我第一次就踩了:
DashScope 有两个长得很像的地址,用途完全不同:
| 地址 | 传什么 | 谁在用 |
|---|---|---|
/api/v1/apps/{APP_ID}/completion |
app_id + prompt,调平台上建好的应用 |
平台应用调用 |
/api/v1/services/aigc/text-generation/generation |
model + messages,直连模型 |
SDK、Spring AI、LangChain4j 全走这条 |
很多教程给的 curl 是前者(带 APP_ID),但代码里要的是后者。照抄 curl 到 Java 里 404,八成就是这个原因。
HTTP 直连真正的价值不在生产,而在于:它是唯一能让你看见"框架到底替你做了什么"的方式。把这条 curl 跑通一次,后面看 Spring AI 的报错会清晰很多。
五、方式三:Spring AI(推荐日常用)
先说明一个前提:Spring AI 官方对国产模型覆盖不全 ,很多只提供 OpenAI 兼容接入。所以在 Spring Boot 里接通义,实际用的是 Spring AI Alibaba------它是 Spring AI 的扩展,用法完全一致。
xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
注意:这个版本要额外加仓库,否则依赖解析不了。
xml
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
配置只有三行:
yaml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
model: qwen-plus
代码分两层,别搞混:
java
// 第一层:ChatModel ------ 底层,够简单就直接用
@Resource
private ChatModel dashscopeChatModel;
AssistantMessage output = dashscopeChatModel
.call(new Prompt("你是谁?"))
.getResult()
.getOutput();
System.out.println(output.getText());
java
// 第二层:ChatClient ------ 高层,带记忆、Advisor、流式,业务里用它
ChatClient chatClient = ChatClient.builder(dashscopeChatModel).build();
String content = chatClient.prompt()
.user("你是谁?")
.call()
.content();
两种调用方式的区别,用一句话记住:ChatModel 是引擎,ChatClient 是方向盘。
引擎能发动车,但你要拐弯、要开空调,得靠方向盘。后面课程里的多轮记忆、RAG、工具调用,全是往 ChatClient 上挂。
Spring AI 最大的价值是最后这一点:
换模型只改一行配置。 从
qwen-plus换到deepseek-chat,代码一个字不动------前提是这家模型有对应的 starter。
代价是多了一层抽象,报错信息经常被包一层,排查时要学会往上翻 cause。
六、方式四:LangChain4j(代码最短)
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope</artifactId>
<version>1.0.0-beta2</version>
</dependency>
java
ChatModel model = QwenChatModel.builder()
.apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
.modelName("qwen-plus")
.build();
String answer = model.chat("你是谁?");
五行。 这是四种里最少的。
代价是:LangChain 官方不支持 阿里系模型,只能用社区版(community 包)。社区版的通病是------版本号带 -beta、更新节奏跟着主版本走、文档比主库少一大截。
顺带一个版本坑:老教程里写的是
ChatLanguageModel,新版本已经统一叫ChatModel。跟着两年前的博客敲,第一个报错就是cannot find symbol。
如果你团队本来就在用 LangChain4j,那没问题;如果是新项目、Spring 技术栈,我不建议为了省这几行代码换生态。
七、怎么选:一张图对号入座

按场景对号入座
补一句图里没写的:别用"哪个更强"来选,用"我的下一步要做什么"来选。
- 下一步是做 RAG、工具调用、多轮对话 → Spring AI,因为这些东西它内置
- 下一步只是"把模型接进来试试" → SDK,够用且最省心
- 下一步要写网关、要跨语言 → HTTP
- 下一步要做复杂链式编排 → LangChain4j
八、真正拉开差距的:加一个「多轮对话」
上面都是单轮。但真实的 AI 应用几乎都要多轮------用户问「那它多少钱」,模型得记得「它」指什么。
这一加,四种方式的差距立刻出来了:
| 方式 | 要改什么 | 大概工作量 |
|---|---|---|
| 官方 SDK | 自己维护一个 List<Message>,每次请求把历史全塞进去,还要自己控制长度不超 token |
+15 行,且容易写出内存泄漏 |
| HTTP 直连 | 同上,还得手动拼 JSON 数组、算 token | +20 行 |
| Spring AI | 加一个 Advisor | +3 行 |
| LangChain4j | 用 AiServices 声明一个接口,框架代理实现 |
+5 行 |
Spring AI 那三行长这样:
java
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(20) // 只保留最近 20 条
.build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
之后的每一轮对话,框架自动帮你做三件事:读历史 → 拼进 prompt → 把新的一轮写回存储。你只写业务。
注意
MessageChatMemoryAdvisor.builder(chatMemory),不是new MessageChatMemoryAdvisor(...)。老版本是构造器,新版本统一成了 builder------又一个版本坑。
这就是我说「真正写业务时切 Spring AI」的实际含义:省下来的不是最初那 20 行,而是后面每一次加功能时的重复劳动。
九、5 个坑,全是我踩过的
坑 1:教程的版本,和现在的版本对不上
这是最要命的一个。你搜到的教程大多写于 2025 年初,那时 Spring AI 还在 1.0.0-M6,starter 叫 spring-ai-openai-spring-boot-starter;现在早就变了。
| 组件 | 2025 年初教程 | 截至 2026 年 11 月 |
|---|---|---|
| Spring AI | 1.0.0-M6,starter 名 spring-ai-*-spring-boot-starter |
1.0.x / 1.1.x / 2.0.x,starter 名 spring-ai-starter-model-* |
| Spring AI Alibaba | 1.0.0-M6.1,坐标 spring-ai-alibaba-starter |
1.0.0.2 GA 起,1.1.x 线拆成 spring-ai-alibaba-starter-dashscope 等 |
| LangChain4j | 1.0.0-beta2,接口 ChatLanguageModel |
主库 1.x,community 已到 1.22.0-beta32,接口统一为 ChatModel |
| dashscope-sdk-java | 2.19.1 |
坐标不变,版本号持续更新 |
判断口诀:编译不过先怀疑版本,别怀疑自己。 去 Maven 中央仓库看一眼当前版本号,比在报错里泡半小时有用。
坑 2:spring-ai 依赖一直解析失败
早期版本没发到中央仓库,必须加 spring-milestones 仓库(见上面第五节)。报错长得像这样:
Could not resolve dependencies ... spring-ai-core:jar:1.0.0-M6
不是网络问题,是仓库没配。
坑 3:base-url 多写一个 /v1,直接 404
用 OpenAI 兼容模式接通义时,地址是:
arduino
https://dashscope.aliyuncs.com/compatible-mode
结尾停在 compatible-mode,不要自作多情加 /v1。 我在这上面排查了快两小时,最后发现就是多敲了四个字符。
坑 4:把「应用调用」的地址当成「模型调用」
就是第四节那张表。/apps/{APP_ID}/completion 和 /services/aigc/.../generation 是两个接口,参数结构完全不同。
坑 5:Key 写进 yml,然后推到了 Git
提交前检查一遍,.gitignore 里应该有:
gitignore
target/
.idea/
application-local.yml
.env
已经提交了也别慌------去控制台把这个 Key 删掉重新生成,光删 Git 记录没用,历史提交里还在。
顺手整理了一张报错对照表,搜报错的人可以直接命中:
| 报错原文 | 真实原因 | 怎么修 |
|---|---|---|
Could not resolve dependencies ... spring-ai-core:jar |
早期版本没发中央仓库 | 加 spring-milestones 仓库 |
401 Unauthorized / InvalidApiKey |
Key 错,或环境变量没生效 | setx 之后重开 IDEA ;终端 echo $env:AI_DASHSCOPE_API_KEY 验证 |
404 Not Found |
base-url 多了 /v1,或误用了 /apps/{APP_ID} 接口 |
换回 /compatible-mode 或 /services/aigc/... |
cannot find symbol: ChatLanguageModel |
LangChain4j 新版改名 ChatModel |
改类型名,或锁定老版本 |
No qualifying bean of type 'ChatModel' |
starter 没引,或包扫描不到 | 检查依赖坐标 + @ComponentScan 范围 |
十、我的实际迁移路径(三步)
不是一上来就写 Spring AI,而是分三步,每步都能独立验证:
| 步骤 | 做什么 | 验证标准 |
|---|---|---|
| 1 | 用 SDK 跑通一句「你是谁」 | 控制台能打印模型回复 |
| 2 | 换成 HTTP 跑同一个问题 | 返回的 JSON 结构和 SDK 对得上 |
| 3 | 切到 Spring AI,用 ChatClient 重写 | 代码里不再出现 dashscope 包名 |
第三步有个隐藏好处:切完之后,你的业务代码里就不该再出现任何厂商相关的类名了。 如果还有,说明抽象没做干净,将来换模型会很痛。
十一、速查表(建议截图)
| 我要...... | 用这个 | 关键配置 |
|---|---|---|
| 最快验证 Key 是否可用 | 官方 SDK | resultFormat(MESSAGE) 不能省 |
| 看清请求到底发了什么 | HTTP 直连 | 直连模型用 /services/aigc/.../generation |
| 在 Spring 项目里做 AI 应用 | Spring AI + Alibaba | 加 spring-milestones 仓库 |
| 换模型不改代码 | Spring AI | spring.ai.dashscope.chat.options.model |
| 多轮记忆 / RAG / 工具调用 | ChatClient | ChatModel 是引擎,ChatClient 是方向盘 |
| 代码最短 | LangChain4j 社区版 | 接口叫 ChatModel,不是 ChatLanguageModel |
下一篇想写流式输出:为什么你的 AI 回复总是要等三五秒才「啪」地一下蹦出整段,而别人的是一字一字往外冒。这事和框架无关,和 SSE 有关。