昨天刷掘金满屏都是 Java 25,什么 Pattern Matching、Stable Values、Module Import... 说实话这些特性确实不错,但作为一个每天都在调 AI API 的后端仔,让我更兴奋的反而是另一件事------Spring AI 终于把 Java 调大模型这件事做对了。
之前用 Java 调 GPT、Claude 这些模型,要么手写 OkHttp/RestTemplate 拼 JSON,要么用一堆非官方 SDK 担心跑路。现在 Spring AI 1.0 GA 了,配合 Java 25 的新语法糖,体验直接拉满。
先说结论
| 对比项 | 手写 HTTP | Python openai 库 | Spring AI 1.0 |
|---|---|---|---|
| 接入成本 | 高(拼 JSON、处理流式) | 低(3 行代码) | 低(3 行配置) |
| 类型安全 | 无 | 无 | 有(编译期检查) |
| 切换模型 | 改一堆代码 | 改 model 参数 | 改一行配置 |
| 切换厂商 | 重写 | 改 base_url | 改一行配置 |
| 流式响应 | 手动处理 SSE | 简单 | Flux 原生支持 |
| Spring 生态集成 | 自己搞 | 不存在 | 原生(DI、AOP、配置中心) |
一句话:Java 调大模型的体验,终于追上 Python 了。某些方面甚至更好。
从 0 到跑通:10 分钟搞定
第一步:建项目
Java 25 + Spring Boot 3.5 + Spring AI 1.0,用 start.spring.io 勾一下就行。如果你习惯手动搞,pom.xml 加这些:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
第二步:配置(重点来了)
application.yml 只需要 3 行:
yaml
spring:
ai:
openai:
api-key: ${AI_API_KEY}
base-url: https://api.openai.com/v1
chat:
options:
model: gpt-5-mini
temperature: 0.7
这里的关键在于 base-url------Spring AI 用的是 OpenAI 兼容协议,意味着任何兼容 OpenAI 协议的服务都能直接用。后面会细说这个。
第三步:写代码
java
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
没了。不是简化版,这就是完整代码。跑起来访问 /chat?message=你好 就能拿到 AI 回复。
要是之前用 OkHttp 写,光拼请求体、解析响应就得 50 行起步,还得处理各种边界情况。
Java 25 新语法 + Spring AI 的化学反应
Java 25 的几个新特性和 Spring AI 配合起来特别舒服。
Pattern Matching 处理多模型响应
java
// Java 25 的增强 Pattern Matching,处理不同类型的 AI 响应
public String handleResponse(Generation generation) {
var result = generation.getResult();
return switch (result) {
case AssistantMessage msg when msg.hasToolCalls() ->
processToolCalls(msg.getToolCalls());
case AssistantMessage msg when msg.getContent().length() > 5000 ->
summarize(msg.getContent());
case AssistantMessage msg ->
msg.getContent();
default -> "未知响应类型";
};
}
比起之前的 if-else 嵌套,这个可读性好太多了。
Record + Structured Output = 类型安全的 AI 输出
这是我觉得 Spring AI 最香的功能------结构化输出。配合 Java 的 Record 类型:
java
// 定义你想要的输出结构
record CodeReview(
String summary,
List<Issue> issues,
int score
) {
record Issue(String file, int line, String severity, String description) {}
}
// AI 直接返回强类型对象,不用手动 parse JSON
@GetMapping("/review")
public CodeReview reviewCode(@RequestParam String code) {
return chatClient.prompt()
.user("Review this code and identify issues:\n" + code)
.call()
.entity(CodeReview.class);
}
返回值直接就是 CodeReview 对象,Spring AI 在底层帮你把 AI 的 JSON 输出反序列化成强类型。如果 AI 返回的格式不对,它会自动重试。
这在 Python 里得用 Pydantic + instructor 才能做到,Java 原生 Record 就够了。
实战:3 个真实场景
场景 1:智能客服(流式输出)
java
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
return chatClient.prompt()
.system("你是一个专业的客服助手,回答要简洁准确")
.user(message)
.stream()
.content();
}
前端用 EventSource 接收就行,打字机效果直接就有了。底层走的是 Reactor 的 Flux,和 Spring WebFlux 天然集成,不用自己搞 SSE。
场景 2:批量文档摘要(并发优化)
java
@Service
public class DocumentSummarizer {
private final ChatClient chatClient;
public List<String> summarizeBatch(List<String> documents) {
return documents.parallelStream()
.map(doc -> chatClient.prompt()
.user("用一句话总结这段文档:\n" + doc)
.call()
.content())
.toList();
}
}
Java 25 的虚拟线程加持下,parallelStream 跑 AI 调用不会阻塞平台线程。100 个文档并发摘要,线程池都不用配。
场景 3:Function Calling(让 AI 调你的 API)
java
@Bean
@Description("查询指定城市的实时天气")
public Function<WeatherRequest, WeatherResponse> getWeather() {
return request -> weatherService.query(request.city());
}
record WeatherRequest(String city) {}
record WeatherResponse(String city, double temp, String condition) {}
注册一个 Bean,加个 @Description 注解,Spring AI 就会自动把这个函数暴露给大模型。用户问"北京今天天气怎么样",AI 会自动调用 getWeather("北京"),拿到结果后再组织语言回复。
这个功能之前要手写一堆 JSON Schema 描述,现在 Spring AI 从 Record 的字段名自动推断,省了一大坨模板代码。
踩坑记录
坑 1:OpenAI 官方 API 在国内延迟感人
测试环境用 OpenAI 官方 API 没问题,但上了国内服务器后首 token 延迟 3-8 秒,流式输出断断续续。
解决办法:换一个兼容 OpenAI 协议的国内接口。Spring AI 的好处就在这------只改配置,代码一行不动:
yaml
spring:
ai:
openai:
api-key: ${OFOX_API_KEY}
base-url: https://api.ofox.ai/v1
chat:
options:
model: gpt-5-mini
改完 base-url 和 api-key 就行了。我用 ofox.ai 的聚合接口测了一下,首 token 延迟从 5 秒降到了 300ms 左右,而且同一套配置可以切 GPT、Claude、Gemini 各种模型,不用换 SDK。
坑 2:Structured Output 偶尔解析失败
AI 返回的 JSON 有时候会多一些字段或者格式不对,导致反序列化失败。Spring AI 默认会重试一次,但如果你的 Record 定义比较复杂,建议加个 @JsonIgnoreProperties(ignoreUnknown = true):
java
@JsonIgnoreProperties(ignoreUnknown = true)
record CodeReview(String summary, List<Issue> issues, int score) {
// ...
}
坑 3:Spring AI 的 BOM 版本别搞混
Spring AI 1.0.x 和 Spring Boot 3.5.x 配套使用。如果你还在 Spring Boot 3.3,需要用 Spring AI 0.8.x。版本不匹配会报一堆 NoSuchMethodError,debug 到怀疑人生。
坑 4:流式输出的错误处理
stream() 返回的 Flux 如果中途 AI 报错,默认会直接断掉连接。建议加个 onErrorResume:
java
return chatClient.prompt()
.user(message)
.stream()
.content()
.onErrorResume(e -> Flux.just("[AI 响应异常,请重试]"));
和 Python 方案的对比
说实话,写这篇文章之前我一直觉得 Java 调 AI 不如 Python 方便。但实际用下来,Spring AI 在几个方面甚至超过了 Python:
- 类型安全:Record + Structured Output 比 Pydantic 更简洁
- 并发能力:虚拟线程 + Reactor 碾压 Python 的 asyncio
- 工程化:配置中心、健康检查、指标监控全是现成的
- 切换厂商:改一行 YAML 配置 vs Python 里可能要换 SDK
Python 赢在原型速度------写个脚本调一下 API,Python 确实更快。但一旦要上生产、要维护,Java + Spring AI 的工程化优势就出来了。
小结
Java 25 + Spring AI 1.0 这个组合确实让 Java 在 AI 开发领域翻身了。以前 Java 同学看 Python 同学三行代码调 GPT 只能流口水,现在 Java 也是三行配置的事。
而且 Spring AI 最聪明的设计是用 OpenAI 兼容协议做底层------你今天用 GPT,明天想换 Claude 或者国产模型,改一行配置就行,代码完全不动。这对企业项目来说太重要了。
如果你是 Java 开发者,强烈建议现在就试试 Spring AI。真不是让你放弃 Python,而是该用 Java 的场景终于不用为了调个 AI 硬切 Python 了。