跳到主要内容
Spring AI 1.1.0 基础使用:构建智能应用实战 | 极客日志
Java AI java
Spring AI 1.1.0 基础使用:构建智能应用实战 介绍基于 Spring Boot 3.5.0 和 Spring AI 1.1.0 的大模型集成方案。涵盖项目依赖配置、通义千问接入、ChatClient 同步与流式调用、系统提示词设置及结构化输出(JSON 转 Java 对象)。包含生产级错误处理、性能优化建议及常见问题解答,帮助开发者快速构建智能应用。
GitMaster 发布于 2026/3/29 更新于 2026/5/23 22 浏览引言
在 AI 快速发展的今天,集成大语言模型(LLM)成为了现代应用开发的必备技能。Spring AI 是 Spring 官方提供的一套标准化框架,用于简化与 LLM 的集成开发。本文通过 Spring Boot 3.5.0 + Spring AI 1.1.0 + 通义千问(Qwen)的技术栈,深入讲解 Spring AI 的基础使用方法。
我们以真实的课程项目为例,展示如何从项目构建、基础对话、流式输出、系统提示词,到结构化输出的完整开发流程。无论你是初学者还是有一定经验的 Java 开发者,都能从本文获得实用的开发经验。
一、项目搭建:依赖配置与应用初始化
1.1 Maven 依赖配置
首先,我们需要在 pom.xml 中配置 Spring AI 的相关依赖。这是项目的基石,关系到后续所有功能的可用性。
<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns ="http://maven.apache.org/POM/4.0.0" >
<parent >
<groupId > org.artisan</groupId >
<artifactId > artisan-ai-agent</artifactId >
<version > 1.0-SNAPSHOT</version >
</parent >
<artifactId > spring-ai-demo</artifactId >
<properties >
<maven.compiler.source > 21</maven.compiler.source >
<maven.compiler.target > 21</maven.compiler.target >
<project.build.sourceEncoding > UTF-8</project.build.sourceEncoding >
</properties >
org.springframework.boot
spring-boot-starter-web
org.springframework.ai
spring-ai-starter-model-openai
org.springframework.ai
spring-ai-starter-model-chat-memory-repository-jdbc
mysql
mysql-connector-java
8.0.33
org.springframework.ai
spring-ai-starter-vector-store-elasticsearch
org.springframework.ai
spring-ai-rag
org.springframework.ai
spring-ai-bom
1.1.0
pom
import
<dependencies >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
<version >
</version >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
</dependencies >
<dependencyManagement >
<dependencies >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
<version >
</version >
<type >
</type >
<scope >
</scope >
</dependency >
</dependencies >
</dependencyManagement >
</project >
spring-ai-starter-model-openai :虽然名称包含 "openai",但它使用 OpenAI 协议标准,支持任何兼容 OpenAI API 的服务,包括阿里云 DashScope、DeepSeek 等。
spring-ai-bom :通过 Bill of Materials 统一管理 Spring AI 生态中所有模块的版本,避免版本冲突。
Java 21 :Spring AI 1.1.0 要求 Java 17+,我们选择 Java 21 以获得最新特性。
1.2 应用启动类配置 应用启动类的职责是初始化 Spring AI 的核心组件,特别是 ChatClient。
package com.artisan;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor;
import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringAIApplication {
@Bean
public ChatClient chatClient (ChatClient.Builder chatClientBuilder) {
return chatClientBuilder.build();
}
@Bean
public ChatMemory chatMemory (JdbcChatMemoryRepository chatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.build();
}
@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor () {
return new DefaultToolExecutionExceptionProcessor (false );
}
public static void main (String[] args) {
SpringApplication.run(SpringAIApplication.class, args);
}
}
ChatClient :这是一个高级抽象,内部集成了模型调用、流处理、内存管理等功能。通过 Builder 模式提供灵活的配置接口。
ChatMemory :基于消息窗口的内存管理策略,自动维护最近的 N 条消息,既能保留上下文,又能避免 token 浪费。
ToolExecutionExceptionProcessor :当 LLM 调用工具出现异常时的处理策略,设为 false 表示优雅处理,不中断流程。
1.3 配置文件:接入通义千问 Spring AI 的配置集中在 application.yml 中。关键是通过 OpenAI 兼容模式接入阿里云 DashScope。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/my_db?useUnicode=true&characterEncoding=utf-8
username: root
password: artisan123456
ai:
openai:
base-url: https://dashscope.aliyuncs.com/compatible-mode
api-key: ${DASHSCOPE_API_KEY}
chat:
options:
model: qwen3-max
embedding:
options:
model: text-embedding-v4
server:
port: 8081
阿里云 DashScope 提供的 OpenAI 兼容模式使得开发者无需修改代码就能切换模型提供商。当需要切换到其他服务(如 DeepSeek)时,仅需修改 base-url 即可:
ai:
openai:
base-url: https://api.deepseek.com
api-key: ${DEEPSEEK_API_KEY}
chat:
options:
model: deepseek-chat
二、基础对话:ChatClient 的同步调用 最简单的方式是进行同步对话。用户发送问题,等待 LLM 返回完整的回答。
@RestController
public class ArtisanController {
@Autowired
private ChatClient chatClient;
@GetMapping("/chat")
public String chat (String question) {
return chatClient.prompt(question).call().content();
}
}
chatClient.prompt(question) └─> 创建一个 PromptRequest 对象,包含用户问题
.call() └─> 发起同步 HTTP 请求到 DashScope API └─> 阻塞等待 LLM 返回完整响应
.content() └─> 从 ChatClientResponse 中提取消息内容 └─> 返回字符串格式的回答
特性 说明 优点 实现简单,逻辑清晰,适合后端批处理 缺点 阻塞等待,响应时间长,用户体验差 适用场景 异步任务、定时任务、消息队列处理
curl "http://localhost:8081/chat?question=你好,请自我介绍"
三、流式输出:Flux 与 SSE 的三种实现方式 现代 AI 应用需要实时显示 LLM 的输出过程,而不是等待完整响应。Spring AI 提供了两种流式处理方案。
3.1 方案一:Flux 流式响应 最简洁的方案是直接返回 Flux,由 Spring WebFlux 负责流式传输。
@GetMapping(value = "/stream", produces = "text/html;charset=UTF-8")
public Flux<String> stream (String question) {
return chatClient.prompt(question).stream().content();
}
<script >
const eventSource = new EventSource ('/stream?question=请写一首诗' );
eventSource.addEventListener ('message' , (event ) => {
document .body .innerHTML += event.data ;
});
eventSource.addEventListener ('error' , () => {
console .error ('连接异常' );
eventSource.close ();
});
</script >
3.2 方案二:SseEmitter 自定义编码 某些场景下需要完全控制 HTTP 响应头,特别是确保 UTF-8 编码。此时使用 SseEmitter 并自定义响应头。
@GetMapping(value = "/sse")
public SseEmitter sse (String question) {
SseEmitter sseEmitter = new SseEmitter () {
@Override
protected void extendResponse (ServerHttpResponse outputMessage) {
HttpHeaders headers = outputMessage.getHeaders();
headers.setContentType(new MediaType ("text" , "event-stream" , StandardCharsets.UTF_8));
}
};
Flux<String> stream = chatClient.prompt(question).stream().content();
stream.subscribe(
token -> {
try {
sseEmitter.send(token);
} catch (IOException e) {
sseEmitter.completeWithError(e);
}
},
sseEmitter::completeWithError,
sseEmitter::complete
);
return sseEmitter;
}
响应头自定义 :通过 extendResponse() 方法在发送第一个 SSE 消息前设置响应头,确保 UTF-8 编码。
Flux 订阅 :使用 Reactor 的三参数订阅方式,分别处理成功、错误、完成三种事件。
异常处理 :网络异常时主动调用 completeWithError() 通知客户端。
3.3 方案三对比:何时选择哪个方案 方案 实现复杂度 控制粒度 推荐场景 Flux 直接返回 低 低 简单场景、不需要特殊编码处理 SseEmitter 中 高 需要自定义响应头、特殊编码处理
四、系统提示词:定义 AI 的角色与行为 系统提示词(System Prompt)是指导 LLM 行为的关键。通过在每次请求前明确定义 LLM 的身份与行为准则,可以显著提升回答质量。
@GetMapping("/system")
public String system (String question) {
return this .chatClient
.prompt()
.system("你是周瑜老师" )
.user(question)
.call()
.content();
}
.system("你是一个助手" )
.system("""
你是一名资深的 Java 开发者。
你的目标是帮助我学习 Spring AI 框架。
回答时请遵循以下原则:
1. 提供代码示例时必须能正常运行
2. 解释复杂概念时,先给出直观理解,再深入细节
3. 如果不确定答案,请明确说明
""" )
五、结构化输出:从 JSON 到 Java 对象 LLM 通常返回文本,但应用需要结构化数据。Spring AI 提供了两种方案来解决这个问题。
5.1 方案一:BeanOutputConverter + PromptTemplate 这是最灵活的方案,允许完全控制 LLM 的输出格式。
static class Poem {
private String title;
private String author;
private String content;
}
@GetMapping("/output")
public Poem output (String topic) {
BeanOutputConverter<Poem> outputConverter = new BeanOutputConverter <>(new ParameterizedTypeReference <Poem>() {});
PromptTemplate promptTemplate = new PromptTemplate ("写一首关于{topic}的七言绝句,{format}" );
String prompt = promptTemplate.render(Map.of("topic" , topic, "format" , outputConverter.getFormat()));
String content = chatClient.prompt(prompt).call().content();
return outputConverter.convert(content);
}
5.2 方案二:entity() 直接转换 Spring AI 1.1.0 新增了 entity() 方法,可以一行代码实现对象转换。
@GetMapping("/entity")
public Poem entity (String topic) {
PromptTemplate promptTemplate = new PromptTemplate ("写一首关于{topic}的七言绝句" );
String prompt = promptTemplate.render(Map.of("topic" , topic));
return chatClient.prompt(prompt).call().entity(Poem.class);
}
5.3 两种方案对比 特性 BeanOutputConverter entity() 代码简洁度 较复杂 简洁 定制灵活性 高(完全控制格式说明) 低(自动生成) 适用场景 复杂嵌套对象、特殊格式要求 简单对象、快速开发
六、深入理解:ChatClient 的架构设计
6.1 Builder 模式与链式调用 Spring AI 的 ChatClient 采用 Builder 模式和流式接口(Fluent Interface)设计,使代码更易读且易于扩展。
chatClient
.prompt()
.system("系统提示词" )
.user("用户问题" )
.call()
.content();
chatClient
.prompt("问题" )
.stream()
.content();
6.2 ChatClient 内部工作原理 虽然 ChatClient 的使用很简单,但内部实现却相当复杂。
构建请求 :添加系统消息、用户消息、应用 Advisors(拦截器)、配置模型选项(温度、max_tokens 等)。
调用 LLM API :序列化为 OpenAI 格式、发送 HTTP 请求到 DashScope、解析返回的 JSON 响应。
包装返应 :消息内容、元数据(模型名、token 数等)、工具调用信息。
提取内容 :.content() 提取文本内容。
6.3 流式调用的底层机制 流式调用的关键是利用 Reactor 框架的 Flux。
chatClient.prompt().stream().content()
Flux<String> streamContent () {
return Flux.create(emitter -> {
emitter.next(token);
});
}
非阻塞 :使用 Flux 而不是传统的 Thread.sleep() 或 BlockingQueue
背压处理 :Reactor 自动处理生产者速度 > 消费者速度的情况
异常传播 :流中任何异常都会触发 onError 回调
七、生产级别的实战建议
7.1 错误处理 @GetMapping("/chat")
public ResponseEntity<?> chat(String question) {
try {
String response = chatClient.prompt(question).call().content();
return ResponseEntity.ok(response);
} catch (IllegalStateException e) {
return ResponseEntity.status(500 ).body("AI 服务配置错误:" + e.getMessage());
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.TOO_MANY_REQUESTS) {
return ResponseEntity.status(429 ).body("请求过于频繁,请稍后再试" );
}
return ResponseEntity.status(500 ).body("AI 服务异常:" + e.getMessage());
}
}
7.2 请求超时配置 spring:
ai:
openai:
base-url: https://dashscope.aliyuncs.com/compatible-mode
api-key: ${DASHSCOPE_API_KEY}
chat:
options:
model: qwen3-max
temperature: 0.7
max-tokens: 2048
7.3 成本控制
@GetMapping("/chat-with-metrics")
public Map<String, Object> chatWithMetrics (String question) {
ChatClientResponse response = chatClient.prompt(question).call();
Map<String, Object> result = new HashMap <>();
result.put("answer" , response.getResult().getOutput().getContent());
if (response.getMetadata() != null ) {
result.put("tokenUsage" , Map.of(
"promptTokens" , response.getMetadata().getUsage().getPromptTokens(),
"completionTokens" , response.getMetadata().getUsage().getCompletionTokens(),
"totalTokens" , response.getMetadata().getUsage().getTotalTokens()
));
}
return result;
}
八、常见问题解答
Q1:如何确保中文显示不乱码?
项目编码:pom.xml 中设置 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
响应头:使用 SseEmitter 时显式指定 MediaType("text", "event-stream", StandardCharsets.UTF_8)
数据库连接:MySQL URL 中加入 useUnicode=true&characterEncoding=utf-8
Q2:Flux 和 SseEmitter 有什么区别? 方面 Flux SseEmitter 是否阻塞 非阻塞,基于 Reactor 非阻塞,基于 Servlet 异步 响应头控制 自动处理 可完全自定义 客户端兼容性 需要支持 Server-Sent Events 标准 SSE,广泛支持 推荐使用 Spring Boot 3.0+ 需要特殊控制时
Q3:如何切换不同的 LLM 模型? A: 修改 application.yml 中的配置:
spring:
ai:
openai:
base-url: https://api.deepseek.com
api-key: ${DEEPSEEK_API_KEY}
chat:
options:
model: deepseek-chat
spring:
ai:
openai:
base-url: https://api.openai.com/v1
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4-turbo
Q4:如何监控和日志记录 LLM 调用? A: Spring AI 支持通过 Spring AI 的日志观察机制:
spring:
ai:
chat:
client:
observations:
log-prompt: true
log-completion: true
@Bean
public CallAdvisor loggingAdvisor () {
return new CallAdvisor () {
@Override
public ChatClientResponse adviseCall (ChatClientRequest request, CallAdvisorChain chain) {
logger.info("请求内容:{}" , request.prompt());
ChatClientResponse response = chain.nextCall(request);
logger.info("响应内容:{}" , response.getResult().getOutput().getContent());
return response;
}
@Override
public String getName () {
return "LoggingAdvisor" ;
}
};
}
九、性能优化建议
9.1 连接复用 ChatClient 是线程安全的,建议全局使用单一实例:
@Bean
public ChatClient chatClient (ChatClient.Builder builder) {
return builder.build();
}
public void chat () {
ChatClient client = new ChatClient ();
}
9.2 缓存策略 对于相同的问题,可以使用缓存避免重复调用 LLM:
private final Cache cache = new ConcurrentHashMapCache ();
@GetMapping("/cached-chat")
public String cachedChat (String question) {
return cache.computeIfAbsent(question, q -> {
return chatClient.prompt(q).call().content();
});
}
9.3 批量处理 @PostMapping("/batch-chat")
public ResponseEntity<?> batchChat(@RequestBody List<String> questions) {
return ResponseEntity.ok(
questions.parallelStream()
.map(q -> chatClient.prompt(q).call().content())
.collect(Collectors.toList())
);
}
十、总结与展望
项目搭建 :通过 Maven 依赖和配置文件快速集成 Spring AI
三种调用模式 :同步调用、Flux 流式、SseEmitter 自定义
系统提示词 :指导 LLM 行为的强大工具
结构化输出 :使用 BeanOutputConverter 和 entity() 获取 Java 对象
架构理解 :ChatClient 的 Builder 模式与内部工作原理
生产级建议 :错误处理、超时、成本控制、监控
Spring AI 生态极其丰富,本文只涉及核心基础。后续可深入学习:
RAG(检索增强生成) :结合向量数据库,让 LLM 能够查询知识库
函数调用 :让 LLM 能够主动调用外部工具和 API
多模态 :支持图像、音频等非文本输入
智能体(Agent) :让 LLM 能够自主规划和执行任务
记忆管理 :构建具有长期记忆的对话系统
参考资源
完整示例项目
export DASHSCOPE_API_KEY=your-api-key
cd spring-ai-demo
mvn spring-boot:run
curl "http://localhost:8081/chat?question=你好"
curl "http://localhost:8081/stream?question=请写一首诗"
curl "http://localhost:8081/entity?topic=春天"
关键代码速查表 功能 代码片段 说明 基础对话 chatClient.prompt(q).call().content()同步获取完整回答 流式输出(Flux) chatClient.prompt(q).stream().content()返回 Flux 流式输出(SSE) new SseEmitter() + stream.subscribe()完全控制响应头 系统提示词 .system("你是...").user(q)定义 AI 角色 结构化输出 outputConverter.convert(json)JSON 转 Java 对象 直接转换 .call().entity(Poem.class)一行代码转换
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online