跳到主要内容
Java AI java
SpringAI 会话记忆实现:基于 MySQL 的持久化存储方案 SpringAI 提供 ChatMemory 组件管理多轮对话上下文。本文对比内存存储与 JDBC 持久化存储两种方案,详解核心接口 ChatMemory、MessageWindowChatMemory 及 Repository 实现。通过代码示例展示如何在 Spring Boot 中配置 InMemoryChatMemoryRepository 和 JdbcChatMemoryRepository,涵盖依赖引入、数据库建表、Bean 配置及使用细节。针对开发测试与生产环境给出选型建议,帮助开发者快速落地会话记忆功能。
山野来信 发布于 2026/3/25 更新于 2026/5/8 6 浏览SpringAI 会话记忆实现:基于 MySQL 的持久化存储方案
在 AI 对话场景中,上下文记忆是提升交互体验的核心。如果每次对话都是'失忆式沟通',用户需要重复说明背景,AI 的回复也会脱离场景。SpringAI 提供了会话记忆(Chat Memory)组件 ,支持多轮对话的上下文管理。
本文将聚焦两种最常用的会话记忆存储方式:默认内存存储 (适合开发测试)和 JDBC 持久化存储 (适合生产环境),从实现原理、代码配置到场景选型,帮你快速落地 SpringAI 的会话记忆功能。
1. 先搞懂:SpringAI 会话记忆的核心结构
在讲具体存储方式前,先理清 SpringAI 会话记忆的核心组件,这是理解两种存储方式的基础。
核心逻辑如下:
ChatMemory :定义会话记忆的基础行为(增/查/清空);
MessageWindowChatMemory :SpringAI 默认的 ChatMemory 实现,支持'消息窗口'(比如只保留最近 20 条消息,避免上下文过长);
ChatMemoryRepository :定义消息的持久化行为,具体存储方式由其实现类决定(内存/JDBC)。
ChatMemory 接口
package org.springframework.ai.chat.memory;
import java.util.List;
import org.springframework.ai.chat.messages.Message;
import org.springframework.util.Assert;
public interface ChatMemory {
String DEFAULT_CONVERSATION_ID = "default" ;
String CONVERSATION_ID = "chat_memory_conversation_id" ;
default void add (String conversationId, Message message) {
Assert.hasText(conversationId, "conversationId cannot be null or empty" );
Assert.notNull(message, "message cannot be null" );
this .add(conversationId, List.of(message));
}
void add (String conversationId, List<Message> messages) ;
List<Message> get (String conversationId) ;
;
}
void
clear
(String conversationId)
MessageWindowChatMemory 实现 该实现维护一个固定大小的消息窗口,确保内存中存储的消息总数不超过指定上限。当消息数量超出限制时,会自动移除较旧的非系统消息。特殊处理 SystemMessage,确保系统指令始终有效且不被意外丢弃。
package org.springframework.ai.chat.memory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.util.Assert;
public final class MessageWindowChatMemory implements ChatMemory {
private static final int DEFAULT_MAX_MESSAGES = 20 ;
private final ChatMemoryRepository chatMemoryRepository;
private final int maxMessages;
private MessageWindowChatMemory (ChatMemoryRepository chatMemoryRepository, int maxMessages) {
Assert.notNull(chatMemoryRepository, "chatMemoryRepository cannot be null" );
Assert.isTrue(maxMessages > 0 , "maxMessages must be greater than 0" );
this .chatMemoryRepository = chatMemoryRepository;
this .maxMessages = maxMessages;
}
@Override
public void add (String conversationId, List<Message> messages) {
Assert.hasText(conversationId, "conversationId cannot be null or empty" );
Assert.notNull(messages, "messages cannot be null" );
List<Message> memoryMessages = this .chatMemoryRepository.findByConversationId(conversationId);
List<Message> processedMessages = process(memoryMessages, messages);
this .chatMemoryRepository.saveAll(conversationId, processedMessages);
}
public static Builder builder () {
return new Builder ();
}
public static final class Builder {
private ChatMemoryRepository chatMemoryRepository;
private int maxMessages = DEFAULT_MAX_MESSAGES;
public Builder chatMemoryRepository (ChatMemoryRepository chatMemoryRepository) {
this .chatMemoryRepository = chatMemoryRepository;
return this ;
}
public Builder maxMessages (int maxMessages) {
this .maxMessages = maxMessages;
return this ;
}
public MessageWindowChatMemory build () {
if (this .chatMemoryRepository == null ) {
this .chatMemoryRepository = new InMemoryChatMemoryRepository ();
}
return new MessageWindowChatMemory (this .chatMemoryRepository, this .maxMessages);
}
}
}
ChatMemoryRepository 接口 package org.springframework.ai.chat.memory;
import java.util.List;
import org.springframework.ai.chat.messages.Message;
public interface ChatMemoryRepository {
List<String> findConversationIds () ;
List<Message> findByConversationId (String conversationId) ;
void saveAll (String conversationId, List<Message> messages) ;
void deleteByConversationId (String conversationId) ;
}
2. 方式 1:默认内存存储 这是 SpringAI 的默认会话记忆存储方式,无需额外依赖,消息直接存在 JVM 内存中,适合开发、测试场景。
2.1 核心实现 存储载体为 JVM 堆内存,依赖的 Repository 是 InMemoryRepository (SpringAI 内置,无需手动配置)。它使用 ConcurrentHashMap 作为底层存储,提供线程安全的对话历史管理。
2.2 快速上手代码
引入 SpringAI 依赖 在 pom.xml 中添加 SpringAI 核心依赖(以阿里模型为例,其他模型同理):
<dependency >
<groupId > com.alibaba.cloud.ai</groupId >
<artifactId > spring-ai-alibaba-starter-dashscope</artifactId >
</dependency >
实现一个对话的类,基于内存实现记忆 @Component
@Slf4j
public class LoveApp {
private final ChatClient chatClient;
private static final String SYSTEM_PROMPT = "扮演深耕恋爱心理领域的专家。开场向用户表明身份..." ;
public LoveApp (ChatModel dashscopeChatModel) {
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository ())
.maxMessages(20 )
.build();
chatClient = ChatClient.builder(dashscopeChatModel)
.defaultSystem(SYSTEM_PROMPT)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
public String doChat (String message, String chatId) {
ChatResponse chatResponse = chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, chatId))
.call()
.chatResponse();
String content = chatResponse.getResult().getOutput().getText();
log.info("content: {}" , content);
return content;
}
}
编写测试类 @Test
void testChat () {
String chatId = UUID.randomUUID().toString();
String message = "你好,我是程序员" ;
String answer = loveApp.doChat(message, chatId);
message = "我想让另一半更爱我" ;
answer = loveApp.doChat(message, chatId);
Assertions.assertNotNull(answer);
message = "我的另一半叫什么来着?刚跟你说过,帮我回忆一下" ;
answer = loveApp.doChat(message, chatId);
Assertions.assertNotNull(answer);
}
运行后控制台会输出 AI 的回复,确认多轮对话上下文已正确传递。
2.3 优缺点
优点 :零配置,直接用;内存操作速度快,适合开发调试。
缺点 :服务重启后消息丢失;不支持多实例共享会话(内存是进程隔离的);消息过多会占用 JVM 内存,存在 OOM 风险。
3. 方式 2:JDBC 持久化存储 将会话消息存储到关系型数据库(MySQL、PostgreSQL 等),适合生产环境(数据持久化、支持多实例共享)。
3.1 核心实现 存储载体为关系型数据库,依赖的 Repository 是 JdbcChatMemoryRepository (SpringAI 提供的 JDBC 实现)。其内部会根据配置的数据库类型自动适配 SQL 脚本。
3.2 快速上手代码
引入依赖 在 pom.xml 中添加 SpringJDBC 和数据库驱动(以 MySQL 为例):
<dependency >
<groupId > com.mysql</groupId >
<artifactId > mysql-connector-j</artifactId >
<scope > runtime</scope >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-jdbc</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-model-chat-memory-repository-jdbc</artifactId >
</dependency >
创建数据库表 在 MySQL 中创建会话消息表(字段包含会话 ID、消息内容、角色、时间戳):
CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY (
conversation_id VARCHAR (36 ) NOT NULL COMMENT '对话 ID' ,
content LONGTEXT NOT NULL COMMENT '消息内容' ,
type VARCHAR (10 ) NOT NULL COMMENT '消息类型 (USER/ASSISTANT/SYSTEM/TOOL)' ,
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ,
CONSTRAINT chk_type CHECK (type IN ('USER' , 'ASSISTANT' , 'SYSTEM' , 'TOOL' ))
) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT= 'Spring AI 对话记忆表' ;
CREATE INDEX SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_TIMESTAMP_IDX ON SPRING_AI_CHAT_MEMORY(conversation_id, timestamp DESC );
注意 :如果你使用 MySQL 是需要手动执行这个 SQL 的,但是有一部分数据库是不需要手动执行这个 SQL 的,因为这个脚本本身就包含在依赖里了。路径通常在 classpath:org/springframework/ai/chat/memory/repository/jdbc。
当然,如果你要自动创建脚本时也需要在 yml 文件中进行配置:
默认值:embedded(推荐开发测试用)
spring:
ai:
chat:
memory:
repository:
jdbc:
initialize-schema: embedded
always(仅临时测试非嵌入式数据库用)
spring:
ai:
chat:
memory:
repository:
jdbc:
initialize-schema: always
never(生产环境首选,搭配 Flyway/Liquibase)
spring:
ai:
chat:
memory:
repository:
jdbc:
initialize-schema: never
配置数据源 在 application.yml 中配置数据库连接:
spring:
datasource:
url: jdbc:mysql://localhost:3306/ai_demo?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
配置 JDBC 会话记忆 创建基于 JDBC 的 MessageWindowChatMemory 的 Bean:
package com.example.config;
import lombok.extern.slf4j.Slf4j;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class ChatMemoryConfig {
private static final int DEFAULT_MAX_MESSAGES = 20 ;
@Bean
public ChatMemory chatMemory (JdbcChatMemoryRepository chatMemoryRepository) {
log.info("Initializing JDBC-based MessageWindowChatMemory with maxMessages={}" , DEFAULT_MAX_MESSAGES);
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(DEFAULT_MAX_MESSAGES)
.build();
}
}
在对话中使用 和内存存储的使用方式完全一致(依赖注入 ChatMemory 即可):
package com.example.app;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class LoveApp {
private final ChatClient chatClient;
public LoveApp (ChatModel dashscopeChatModel, ChatMemory chatMemory) {
chatClient = ChatClient.builder(dashscopeChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
public String doChat (String message, String chatId) {
ChatResponse response = chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, chatId))
.call()
.chatResponse();
String content = response.getResult().getOutput().getText();
log.info("content: {}" , content);
return content;
}
}
运行后查询数据库表 SPRING_AI_CHAT_MEMORY,可以看到消息记录已持久化保存。
3.3 优缺点
优点 :数据持久化,服务重启/扩容后消息不丢失;支持多实例共享会话(所有实例连同一个数据库);可通过数据库做消息的持久化分析。
缺点 :需要配置数据库,复杂度高于内存存储;数据库 IO 速度比内存慢(可通过连接池优化)。
4. 两种方式对比与选型建议 维度 内存存储(In-Memory) JDBC 持久化存储 适用场景 开发、测试、临时演示 生产环境 数据持久化 ❌ 重启丢失 ✅ 永久保存 多实例共享 ❌ 进程隔离 ✅ 支持 配置复杂度 低(零配置) 中(需数据库) 性能 高(内存操作) 中(数据库 IO)
开发/测试阶段:用内存存储 ,快速验证功能;
生产环境:用JDBC 存储 ,保证会话数据的可靠性和共享性;
高并发场景:可结合 Redis 缓存(SpringAI 也支持 Redis 存储),进一步提升性能。
5. 总结 SpringAI 的会话记忆通过'接口分层 + 多实现'的设计,让开发者可以灵活切换存储方式:
基础层:ChatMemory 定义会话行为;
实现层:MessageWindowChatMemory 管理消息窗口;
存储层:ChatMemoryRepository 的不同实现(内存/JDBC)决定数据的存储位置。
根据业务需求选择合适的存储策略,能让你的 AI 应用更加稳健可靠。
相关免费在线工具 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