大家好,我是苏三,又跟大家见面了。
最近有位小伙伴问了我一个问题:“三哥,我们现在项目中要接入大模型,我看网上很多教程都是直接用RestTemplate调用OpenAI或者DeepSeek的API,代码也简单,为啥大家都在说Spring AI更香呢?”
我当时笑了笑,问了他一句:你们项目上线后,老板突然说要把大模型从OpenAI换成国产的通义千问,你的代码要改多少行?
他想了一下,沉默了。
今天这篇文章,我就从HTTP调用的痛点出发,跟大家一起聊聊Spring AI。
希望对你会有所帮助。
先带大家感受一下,绝大多数Java开发者第一次接入大模型时都是怎么写的。
以调用DeepSeek API为例,使用原生RestTemplate的实现大致如下:
@RestController
public class NativeAIController {
// 密钥和地址硬编码,这是第一坑
private static final String API_KEY = "sk-xxxxxxxx";
private static final String API_URL = "https://api.deepseek.com/v1/chat/completions";
@GetMapping("/ai/chat")
public String chat(@RequestParam String msg) {
RestTemplate restTemplate = new RestTemplate();
// 1. 手动组装请求参数,结构一复杂就容易出错
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", "deepseek-chat");
requestBody.put("messages", List.of(Map.of("role", "user", "content", msg)));
// 2. 手动构造请求头和鉴权信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(API_KEY);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
// 3. 发送请求并处理响应
try {
ResponseEntity<Map> response = restTemplate.exchange(
API_URL, HttpMethod.POST, entity, Map.class);
Map body = response.getBody();
if (body != null && body.containsKey("choices")) {
List<Map<String, Object>> choices = (List) body.get("choices");
Map<String, Object> choice = choices.get(0);
Map<String, Object> message = (Map) choice.get("message");
return (String) message.get("content");
}
return"未获取到有效响应";
} catch (RestClientException e) {
// 异常处理极其简陋
return"调用异常:" + e.getMessage();
}
}
}
这种代码,一个下午能写十个不同的大模型接口。
但是,我们来看这段代码隐藏的六个致命问题:
有些小伙伴可能会说,上面的代码看着也不复杂,我自己封装一下也能用。没错,如果你的项目只是个人Demo、临时验证或者在本地测试,这种写法足够用了。但一旦进入企业级生产环境,它的问题就会像定时炸弹一样一个个爆出来——密钥硬编码、异常处理随意、切换模型时层层改代码……这些坑我当年创业时踩了一个遍。
第一,密钥硬编码在代码中。 API Key直接写在源码里,代码一旦提交到Git仓库,密钥就彻底暴露了,这在企业开发中是绝对禁止的。
第二,请求参数硬编码。 每个模型的请求参数都不一样,OpenAI用messages,千问用input,文心一言又是另一套结构。切换模型时,你需要在所有调用处修改代码。
第三,异常处理形同虚设。 模型API不稳定是常态,超时、限流、服务降级都需要精细的重试和降级策略,这段代码碰到任何异常就直接崩溃了。
第四,没有连接池管理。 每次调用都new RestTemplate(),频繁建立TCP连接,高并发下资源消耗巨大。虽然RestTemplate内部有连接池,但这里连池都没配置好。
第五,响应处理极其脆弱。 JSON结构稍微变化,代码就直接抛异常。大模型API的响应结构经常会更新,每次更新都意味着全量回归测试。
第六,缺少可观测性。 没有埋点、没有日志、没有调用链追踪,线上出问题时根本没法定位。
有些小伙伴可能会说:那我封装一个工具类,把这些事情都抽象出来不就行了?
来,我们看看如果自己封装,需要做多少事:
public class LLMClient {
// 1. 线程安全的客户端复用
private final HttpClient httpClient;
// 2. 重试策略
private final RetryPolicy retryPolicy;
// 3. 限流器
private final RateLimiter rateLimiter;
// 4. 熔断器
private final CircuitBreaker circuitBreaker;
// 5. 连接池
private final ConnectionPool connectionPool;
// 6. 监控埋点
private final MeterRegistry meterRegistry;
// 7. 日志记录
private final Logger logger;
// 还有请求体构建、多模型适配、流式响应处理...代码轻松上百行
}
再看看Spring AI是怎么做的:
@Service
public class AIChatService {
private final ChatClient chatClient;
public String chat(String question) {
return chatClient.prompt(question).call().content();
}
}
三行代码,搞定全部。
这不是炫技,这是工程抽象的力量。
Spring AI是Spring官方于2024年正式发布的AI开发框架,旨在为Java开发者提供一套标准化、模块化的AI工具链。
Spring官方定位很清晰:降低AI开发门槛、统一Java AI生态、强化企业级AI应用支持。
经过2年迭代,Spring AI 1.0正式版于2025年5月发布,通过JSR-382标准认证,承诺API向后兼容至少三个大版本周期。
发布首周即获得超过15万次下载,GitHub星标数突破3.2万,支持20+种模型架构。
Spring AI的核心设计思想是四层抽象架构,从上到下层层封装,每层都解决特定的工程问题:

模型抽象层: 通过ChatModel、EmbeddingModel等顶层接口定义了与大模型交互的统一契约。无论底层是OpenAI还是Ollama,开发者都调用相同的方法。
适配器层: 各个厂商的适配器实现了这些接口,负责协议转换和差异抹平。Spring AI内置对OpenAI、Azure OpenAI、Anthropic Claude、阿里通义千问、Ollama(支持Llama 3等本地模型)等主流模型的支持。需要增加新模型?写一个新的适配器类即可,对业务代码零改动。
服务编排层: 提供ChatClient的Fluent API、PromptTemplate模板引擎以及对话上下文管理能力,让开发者用最简洁的链式语法完成AI调用。
应用集成层: 通过@EnableAI注解一键激活AI能力,自动配置注入AiClient、ModelRegistry等核心组件,与Spring Security实现模型访问控制。
这是Spring AI最令人惊叹的能力——通过配置文件即可无缝切换底层大模型:
# 方式一:OpenAI
spring:
ai:
openai:
api-key:${OPENAI_API_KEY}
model:gpt-4-turbo
# 方式二:只需换配置——DeepSeek(替换上述配置即可)
spring:
ai:
deepseek:
api-key:${DEEPSEEK_API_KEY}
model:deepseek-chat
# 方式三:本地模型(Ollama跑Llama 3)
spring:
ai:
ollama:
base-url:http://localhost:11434
model:llama3:8b
业务代码一行不用改!API Key通过环境变量注入,完全避免了硬编码问题。
很多开发者会把ChatClient和ChatModel混为一谈,其实它们的定位完全不同:
ChatModel是底层接口,直接与大模型对话,返回原始响应ChatClient是高级封装,提供了Fluent API风格的流式接口,类似Spring WebClient/RestClient的写法@Configuration
public class AIConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultSystem("你是一个专业的AI助手,请用中文回答。")
.defaultOptions(ChatOptions.builder()
.temperature(0.7)
.maxTokens(2000)
.build())
.build();
}
}
在企业级应用中,一个最大的痛点是如何让大模型“调用”我们已有的业务代码——比如查库存、下单、查物流状态等。这就是Spring AI的Function Calling机制的用武之地。
@Component
@Description("根据订单号查询订单详情")
public class OrderQueryFunction implements Function<OrderQueryFunction.Request, OrderQueryFunction.Response> {
@Autowired
private OrderService orderService;
public record Request(@JsonProperty(required = true) String orderId) {}
public record Response(String status, BigDecimal amount, String deliveryTime) {}
@Override
public Response apply(Request request) {
Order order = orderService.getOrder(request.orderId());
return new Response(order.getStatus(), order.getAmount(), order.getDeliveryTime());
}
}
// 在ChatClient中注册工具
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultFunctions("queryOrder") // 注册工具
.build();
用户问“我的订单ORD-001到哪了”,Spring AI自动判断需要调用查询订单函数,将orderId传进去,拿到结果再返回。整个过程对用户透明。
维度 | 手写HTTP | Spring AI |
|---|---|---|
多模型支持 | 每个模型独立封装,切换模型需改多处代码 | 仅需修改配置文件,零代码改动 |
异常/重试/降级 | 手动实现,容易遗漏边界场景 | 框架内置,生产级别稳定 |
流式响应处理 | 手动处理SSE,门槛较高 | chatClient.prompt().stream()一行搞定 |
结构化输出 | JSON手动解析,脆弱且繁琐 | 自动反序列化为Java对象 |
Function Calling | 需自行设计API回调机制 | @Function注解自动注册 |
可观测性 | 需手动集成Micrometer | 原生支持Actuator端点 |
Spring生态集成 | 手动注入Bean | 自动配置,开箱即用 |
开发效率 | 每个接口平均50行样板代码 | 平均5行代码完成调用 |
messages要改成input,整段代码需要重写。ChatModel统一接口暴露。更换底层模型只需在application.yml中修改配置,业务代码一行都不用改。一套代码,适配所有大模型,这才是真正的工程化。企业级应用需要重试、降级、熔断、限流、监控、审计这些非功能性能力。
曾经有一位创业的朋友跟我吐槽:他们自己封装的AI调用模块上线第一天就被流量冲垮了——没有限流熔断,几个大模型同时响应变慢,线程池直接打满,整个核心业务都挂了。重构花了两周,最后还是老老实实上了框架。
spring.retry自动配置,降级通过ModelRouter实现热备切换,熔断通过CircuitBreaker注解声明式配置,限流通过RateLimiter内置过滤器,监控通过Micrometer自动收集模型响应时间、错误率、Token消耗等指标。大语言模型的“流式输出”对于用户体验至关重要,但手写难度不低。
ChatClient的stream()方法和响应式编程模型一行代码搞定:@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String prompt) {
return chatClient.prompt(prompt).stream()
.map(chunk -> chunk.getResult().getOutput().getContent());
}
配合MediaType.TEXT_EVENT_STREAM_VALUE和前端EventSource,丝滑的流式对话体验即刻完成。
在大模型应用中,从模型输出中提取结构化数据是极其常见的场景,比如情感分析、实体抽取等。
StructuredOutputConverter和Java的Record类型,自动将模型输出映射为强类型对象。Spring AI甚至能自动处理格式不匹配的情况——如果AI返回的格式不对,它会自动调整提示词并要求重新生成,直到拿到正确的格式为止,这在Python里是Pydantic才能做到的:public record SentimentResult(String label, double score) {}
BeanOutputConverter<SentimentResult> converter =
new BeanOutputConverter<>(SentimentResult.class);
String json = chatClient.prompt("用户评论:" + review + "。判断情感并返回JSON")
.call().content();
SentimentResult result = converter.convert(json);
// result.label -> "POSITIVE", result.score -> 0.95
Function Calling让大模型能够动态调用外部的业务API,是实现Agent智能体的核心技术。
Function接口并加上@Description和@JsonProperty注解,Spring AI会自动完成函数注册、参数解析和调用路由的全流程。RAG(Retrieval-Augmented Generation)是当前解决大模型知识时效性和幻觉问题的关键方案。
VectorStore抽象层统一对接Elasticsearch、Milvus、PgVector等多种向量数据库,提供DocumentReader自动解析PDF/Word/Excel等格式,通过Retriever接口实现语义+关键词混合检索策略。配置好向量存储后,调用retrieveAndGenerate()即可完成增强生成,所有样板逻辑都由框架代劳。大模型的真正价值在于多轮对话能力,这要求我们能够持续维护和传递上下文。
messages数组,当对话过长时还需要实现截断逻辑(保留最近的N条)防止Token溢出。这些看似简单的功能,实际实现时涉及大量边界条件处理。ConversationContext组件管理多轮对话状态,支持与Redis等缓存系统无缝集成实现跨实例的上下文共享,通过线程安全的ThreadLocal管理每个用户的独立会话。只需调用context.getLastMessages(5)即可获取最近5轮对话,框架自动完成上下文拼接和格式转换,开发者完全不用关心底层的细节。学习曲线与版本稳定性: Spring AI作为一个相对年轻的框架,1.0正式版虽然已发布并承诺向后兼容,但迭代速度仍然较快。LangChain4j和Spring AI都面临版本不稳定、迭代升级快导致的包名及使用方法前后不兼容的问题。
复杂Agent编排能力相对不足: 相比LangChain4j丰富的20+官方插件(LLM调用、向量存储、Web搜索等),Spring AI在开箱即用的Agent工具链上仍有差距。
社区生态仍在培育期: Spring AI虽然GitHub已有3.2万+星标,但与拥有长期社区积累的LangChain4j相比,在插件丰富度和第三方集成上还有提升空间。另外,Spring AI目前依赖社区贡献来扩展新模型的适配器,企业使用特定模型时可能需要自行实现适配器。
最适合选择Spring AI的场景:
慎重使用Spring AI的场景:
写到这里,我想做一个总结:
如果你的目标是“快速写个Demo验证想法”,手写HTTP足以。 它短平快,没有框架负担,能快速跑通流程。
但如果你的目标是“在企业级生产环境中稳定接入大模型,并且业务规模会持续增长”,那Spring AI是目前Java生态中最值得选择的方案。
它为Java开发者带来了一场AI开发的范式转移:
不少从Python转向Java的开发者认为,Java在应对海量用户场景下开发效率低、延迟高。
但Spring AI的出现正在扭转这个局面——它的设计让Java在AI工程化领域从劣势变为优势。
强类型安全减少了运行时错误,成熟的Spring生态提供了现成的工程化组件,JVM的高性能保障了生产环境稳定性。
最终选型,取决于你和你的团队的真实情况。
2026年,Java生态的AI开发格局正在被Spring AI重塑。
下一个用Java写出惊艳AI应用的开发者,或许就是你。