跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
JavaAIjava

SpringAI 会话记忆实现:基于 MySQL 的持久化存储方案

SpringAI 提供 ChatMemory 组件管理多轮对话上下文。本文对比内存存储与 JDBC 持久化存储两种方案,详解核心接口 ChatMemory、MessageWindowChatMemory 及 Repository 实现。通过代码示例展示如何在 Spring Boot 中配置 InMemoryChatMemoryRepository 和 JdbcChatMemoryRepository,涵盖依赖引入、数据库建表、Bean 配置及使用细节。针对开发测试与生产环境给出选型建议,帮助开发者快速落地会话记忆功能。

山野来信发布于 2026/3/25更新于 2026/5/86 浏览
SpringAI 会话记忆实现:基于 MySQL 的持久化存储方案

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 核心依赖(以阿里模型为例,其他模型同理):

<!-- Spring AI Alibaba -->
<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 为例):

<!-- MySQL 驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Spring JDBC -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- Spring AI JDBC Chat Memory Repository -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
创建数据库表

在 MySQL 中创建会话消息表(字段包含会话 ID、消息内容、角色、时间戳):

-- Spring AI JDBC Chat Memory MySQL 表结构
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 文件中进行配置:

  1. 默认值:embedded(推荐开发测试用)
    spring:
      ai:
        chat:
          memory:
            repository:
              jdbc:
                initialize-schema: embedded
    
  2. always(仅临时测试非嵌入式数据库用)
    spring:
      ai:
        chat:
          memory:
            repository:
              jdbc:
                initialize-schema: always
    
  3. 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 应用更加稳健可靠。

目录

  1. SpringAI 会话记忆实现:基于 MySQL 的持久化存储方案
  2. 1. 先搞懂:SpringAI 会话记忆的核心结构
  3. ChatMemory 接口
  4. MessageWindowChatMemory 实现
  5. ChatMemoryRepository 接口
  6. 2. 方式 1:默认内存存储
  7. 2.1 核心实现
  8. 2.2 快速上手代码
  9. 引入 SpringAI 依赖
  10. 实现一个对话的类,基于内存实现记忆
  11. 编写测试类
  12. 2.3 优缺点
  13. 3. 方式 2:JDBC 持久化存储
  14. 3.1 核心实现
  15. 3.2 快速上手代码
  16. 引入依赖
  17. 创建数据库表
  18. 配置数据源
  19. 配置 JDBC 会话记忆
  20. 在对话中使用
  21. 3.3 优缺点
  22. 4. 两种方式对比与选型建议
  23. 5. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Qwen2.5-Math 开源模型实战:从本地部署到奥数题推理验证
  • Ollama 模型管理、删除及 Open-WebUI 本地部署指南
  • MecAgent Copilot:机械设计师的 AI 助手
  • Linux Shell join 命令实战指南
  • 别被“会聊天”的AI骗了!真正的数字助理,应该是ToClaw这样的
  • Android 技术体系化进阶指南:从基础到架构的全方位解析
  • Windows 11 下 WSL2 安装 Ubuntu 22.04 完整指南
  • Python 分析去哪儿旅游攻略数据并制作可视化图表
  • C++位图与布隆过滤器实现及应用
  • Linux 常用网络命令:ping、ifconfig 与 netstat 详解
  • 深入理解 Xilinx FPGA 开发:Vivado 与 Vitis 流程对比
  • 转行 AI 产品经理的核心能力与路径指南
  • OpenWebUI 接入 SearXNG:让本地大模型获取实时信息(含国内引擎配置)
  • 将 AI 小助手接入企业微信:基于回调接口的群聊机器人实现
  • 955 不加班公司名单及工作制说明
  • PicoClaw 轻量级 AI 助手安装与使用指南
  • OpenClaw 配置指南:定制 AI 助手个性
  • OpenClaw Linux/macOS 安装、自启动与基础配置指南
  • Spring Boot WebClient 集成大模型 API:OpenAI、文心一言、通义千问
  • 基于 OpenClaw 搭建 QQ 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