Java转AI第三课:Spring AI如何把100行HTTP代码缩减到10行?
目录
前言
上一篇文章,我们完成了框架选型。
如果还没有看过上一篇,建议先阅读:
📖 《Java转AI第二课:为什么我最终选择Spring AI,而不是LangChain4j?》
上一篇主要回答了一个问题:
为什么最终选择了 Spring AI?
而这一篇,我们来聊另一个更实际的问题。
Spring AI 到底帮我们解决了什么?
很多刚开始学习 AI 的 Java 开发都会有这样的疑问:
调用大模型,不就是发一个 HTTP 请求吗?为什么还要学习一个新的框架?
事实上,我一开始也是这么认为的。
在我的第一个 AI Demo 中,没有使用任何 AI 框架,而是直接通过 RestTemplate 调用 DeepSeek 接口。
聊天功能成功跑起来的时候,我甚至觉得:
"Spring AI 好像也没什么特别的,不就是把 HTTP 封装了一层吗?"
直到后来开始真正做企业项目。
随着功能越来越多,我才发现:
真正复杂的,从来不是调用一次模型。
而是后续不断增加的新能力。
例如:
- 多轮对话(Memory)
- 流式输出(SSE)
- RAG 企业知识库
- Tool Calling
- Agent
- Workflow
这些能力几乎都离不开大模型调用。
如果继续自己维护 HTTP 请求,代码会越来越臃肿。
而 Spring AI 恰恰帮我解决了这个问题。
今天,就结合自己的项目,说说我是如何从一百多行 HTTP 调用,迁移到 Spring AI 的。
1. 一个聊天接口,为什么最后写了一百多行?
很多人第一次调用大模型时,都会觉得非常简单。
无非就是:
用户提问
↓
HTTP 请求
↓
DeepSeek
↓
返回答案
确实如此。
真正的业务代码,其实只有一句:
restTemplate.postForObject(...)
但为了让这一句话真正跑起来,我们还需要完成很多准备工作。
例如:
- 创建 Header
- 配置 Bearer Token
- 设置 Content-Type
- 拼接请求 JSON
- 构建 Message 数组
- 创建请求对象
- 解析返回结果
- 提取真正的回答内容
- 处理异常
- 编写 DTO
下面是一段我最开始调用 DeepSeek 时的核心代码(已做简化)。
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(apiKey);
headers.setContentType(MediaType.APPLICATION_JSON);
Map<String, Object> body = new HashMap<>();
body.put("model", "deepseek-chat");
List<Map<String, String>> messages =
List.of(Map.of("role", "user", "content", question));
body.put("messages", messages);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
DeepSeekResponse response =
restTemplate.postForObject(url, request, DeepSeekResponse.class);
String answer = response.getChoices()
.get(0)
.getMessage()
.getContent();
这只是核心调用。
实际项目中,还需要:
- 请求 DTO
- 响应 DTO
- 配置类
- 日志
- 超时处理
- 异常处理
- 配置读取
全部加起来,一个简单聊天接口就已经接近一百行代码。
当时我觉得:
调第三方接口,不就是这样写的吗?
直到后面不断增加新功能,我才意识到真正的问题。
假设今天增加一个 System Prompt。
我要修改 JSON。
明天增加多轮对话。
我要重新维护 Message。
后天增加流式输出。
整个 HTTP 调用又要重新封装。
真正变化的是业务。
而我却一直在维护同一套 HTTP 请求代码。
慢慢地,我开始意识到:
我需要的已经不是一个 HTTP 工具类,而是一套专门面向 AI 开发的框架。
2. Spring AI 是如何把这些代码隐藏起来的?
后来,我把项目迁移到了 Spring AI。
第一次写代码时,我最大的感受只有两个字:
舒服。
以前,一个聊天接口需要准备各种请求对象。
现在,只需要下面几行代码。
String answer = chatClient
.prompt()
.user(question)
.call()
.content();
第一次看到的时候,我甚至怀疑:
真的就这么简单?
后来才发现,Spring AI 已经帮我们完成了大量重复工作。
例如:
| 传统 HTTP 调用 | Spring AI |
|---|---|
| 创建 Header | 自动完成 |
| 设置 Bearer Token | 自动读取配置 |
| 拼接 JSON | Prompt API |
| 创建 Message | 框架封装 |
| 发送 HTTP 请求 | 自动处理 |
| 解析 Response | content() 获取结果 |
| 异常转换 | 统一处理 |
也就是说。
我们终于不用再关心:
- Header 怎么写?
- JSON 怎么拼?
- Message 怎么组织?
- 返回结果怎么解析?
开发者真正关心的,只剩下一件事。
我要给模型什么 Prompt。
这一点,也是我认为 Spring AI 最大的价值。

3. 真正减少的,不是 90 行代码
很多文章介绍 Spring AI 时,都喜欢说一句话:
Spring AI 可以把一百多行代码缩减到十几行。
这句话当然没错。
但真正做完项目之后,我越来越觉得:
真正减少的,并不是代码。
而是以后所有重复的开发工作。
例如。
如果我要增加 System Prompt。
以前,需要重新修改请求 JSON。
现在。
String answer = chatClient.prompt()
.system(systemPrompt)
.user(question)
.call()
.content();
如果增加历史消息。
String answer = chatClient.prompt()
.messages(messages)
.call()
.content();
如果以后需要切换模型。
以前可能需要重新维护不同厂商的 HTTP 请求。
而现在,只需要切换对应的 ChatModel 或 ChatClient 配置即可,业务代码几乎不需要修改。
这也是 Spring AI 与普通 HTTP 封装最大的区别。
它不仅封装了一次调用。
更重要的是,它统一了整个 AI 应用的开发方式。
4. 企业项目为什么一定会走向 Spring AI?
如果我的项目始终只有一个聊天接口。
说实话,我可能一直都会使用 HTTP 调用。
因为它足够简单,也没有什么维护成本。
真正让我决定迁移到 Spring AI,是项目开始不断增加新的能力。
最开始,我的调用链只有这样:
Controller
│
▼
ChatService
│
▼
HTTP 工具类
│
▼
DeepSeek
后来,项目开始陆续增加更多 AI 能力:
- SSE 流式输出
- 多轮会话 Memory
- RAG 企业知识库
- Tool Calling
- Agent
- Workflow
这时候,我发现问题来了。
这些能力虽然看起来完全不同,但最终都离不开一次大模型调用。
如果继续维护自己的 HTTP 工具类,就意味着每增加一个能力,都要继续扩展底层调用逻辑。
随着项目越来越复杂,HTTP 工具类也会越来越庞大。
迁移到 Spring AI 后,整个调用链变成了下面这样。
Controller
│
▼
ChatService
│
▼
ChatClient
│
├── Prompt
├── Memory
├── Advisor
├── Tool Calling
├── RAG
└── ChatModel
│
▼
DeepSeek / OpenAI / Qwen
可以看到。
无论增加什么能力,业务层始终只需要调用 ChatClient。
真正发生变化的是调用链,而不是业务代码。
这也是 Spring AI 最吸引我的地方。

5. 我的项目是如何一步步演进的?
很多文章介绍 Spring AI,都是直接讲 API。
但真正做项目的时候,并不是一开始就会上 Agent、RAG。
至少我是一步一步走过来的。
整个项目大概经历了下面几个阶段。
5.1 第一阶段:HTTP 调用
只有一个聊天接口。
HTTP
↓
LLM
5.2 第二阶段:Spring AI
迁移到 ChatClient。
ChatClient
↓
LLM
5.3 第三阶段:流式输出
开始支持 SSE。
ChatClient
↓
Stream
↓
SSE
5.4 第四阶段:Memory
增加多轮对话。
chatClient.prompt()
.advisors(new MessageChatMemoryAdvisor(chatMemory))
.user(question)
.call()
.content();
聊天接口没有变化。
只是增加了一个 Advisor。
5.5 第五阶段:RAG
增加企业知识库。
chatClient.prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore))
.user(question)
.call()
.content();
同样。
业务代码没有变化。
只是增加了另一个 Advisor。
5.6 第六阶段:Agent
开始接入 Tool Calling。
chatClient.prompt()
.tools(caseTool, timelineTool)
.user(question)
.call()
.content();
直到这里,我才真正理解 Spring AI 的设计理念。
它不是针对某一个功能设计的。
而是希望所有 AI 能力,都能通过同一种开发方式不断扩展。
对于开发者来说,这种一致性非常重要。

6. 自己封装 HTTP 不好吗?
当然不是。
事实上,我非常建议每个 Java 开发者,在学习 Spring AI 之前,先自己调用一次大模型接口。
原因很简单。
只有真正写过一次 HTTP 请求,你才知道:
- Prompt 最终会被封装成什么样的 JSON?
- Message 是如何组织的?
- 为什么会有 System、User、Assistant 三种角色?
- 模型返回的数据结构是什么?
理解这些之后,再去学习 Spring AI,会轻松很多。
所以,我一直认为:
学习阶段,可以自己写 HTTP。
真正进入企业项目后,再把这些重复工作交给框架。
这样既理解了底层原理,也能享受到框架带来的开发效率。
7. 总结
回过头来看,我觉得 Spring AI 最大的价值,并不是把一百多行代码缩减成十几行。
真正改变我开发方式的,是它提供了一套统一的 AI 编程模型。
无论是聊天、流式输出、Memory、RAG,还是 Agent,都围绕同一个 ChatClient 展开。
业务不断演进,调用方式却保持一致。
对于企业项目来说,这种可扩展性,远比少写几十行代码更重要。
如果让我用一句话总结这一篇,我会这样说:
Spring AI 真正减少的,不是几十行 HTTP 代码,而是未来不断重复的开发成本。
下一篇预告
完成了基础调用之后,我们正式进入 AI 项目中最常见的能力之一——流式输出(SSE)。
下一篇我们会结合项目源码,实现一个真正的流式聊天接口,并重点聊四个问题:
- 为什么 AI 聊天几乎都采用流式输出?
- SSE 和 WebSocket 有什么区别?
- Spring AI 如何实现流式输出?
- 企业项目为什么更推荐 SSE?

📚 推荐专栏
🤖 AI 技术专栏
从 0 到 1 学习 AI 应用开发,持续分享 Spring AI、RAG、Agent、企业级 AI 项目实战。
👉 https://blog.csdn.net/qupengkun/category_13184360.html
🚀 AI 转型日记
记录一名 10 年 Java 开发者从传统后端转向 AI 应用开发的全过程。
👉 https://blog.csdn.net/qupengkun/category_13183497.html
👨💻 关于作者
QCoding
专注 AI 应用开发与 Java 技术实践。
持续分享 Spring AI、RAG、Agent、企业级 AI 项目实战、架构设计与职业成长。
更多推荐





所有评论(0)