You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring AI与Redis集成:查询ChatContextEntity时Prompt实例化失败

问题解决:Spring Data Redis 查询时无法实例化 Prompt 类

问题根源

org.springframework.ai.chat.prompt.Prompt 是 Spring AI 提供的第三方类,没有公开的无参构造函数。Spring Data Redis 默认使用 Jackson 序列化/反序列化时,需要目标类具备无参构造函数来实例化对象,这就是抛出 MappingInstantiationException 的核心原因。你之前编写的 BytesToAddressConverter 无法解决该问题,因为转换器仅处理类型间的格式转换,不负责对象实例化逻辑。

解决方案

方案一:自定义 Jackson 反序列化器(针对 Prompt 类)

通过自定义 Jackson 模块,为 Prompt 类指定反序列化时使用的有参构造函数,绕开无参构造的限制:

  1. 编写自定义 Jackson 模块
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.messages.Message;
import java.io.IOException;
import java.util.List;

public class PromptJacksonModule extends SimpleModule {
    public PromptJacksonModule() {
        addDeserializer(Prompt.class, new PromptDeserializer());
    }

    static class PromptDeserializer extends StdDeserializer<Prompt> {
        protected PromptDeserializer() {
            super(Prompt.class);
        }

        @Override
        public Prompt deserialize(com.fasterxml.jackson.core.JsonParser p, DeserializationContext ctxt) throws IOException {
            // 解析 JSON 中的 messages 字段,用该列表实例化 Prompt
            var node = p.getCodec().readTree(p);
            var messagesNode = node.get("messages");
            List<Message> messages = p.getCodec().treeToValue(messagesNode, List.class);
            return new Prompt(messages);
        }
    }
}
  1. 配置 RedisTemplate,注入自定义模块
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        var objectMapper = jacksonSerializer.getObjectMapper();
        // 注册自定义 Prompt 反序列化模块
        objectMapper.registerModule(new PromptJacksonModule());
        // 忽略未知属性,避免反序列化时因字段不匹配报错
        objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jacksonSerializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

方案二:封装 Prompt 为自定义可序列化类

通过自定义类封装 Prompt 的核心属性,完全控制序列化/反序列化逻辑,避免依赖第三方类的构造限制:

  1. 编写自定义 ChatPrompt 类
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.messages.Message;
import java.util.List;

public class ChatPrompt {
    private List<Message> messages;

    // 无参构造函数,满足 Redis 反序列化要求
    public ChatPrompt() {}

    // 从 Prompt 转换为自定义类的构造函数
    public ChatPrompt(Prompt prompt) {
        this.messages = prompt.getMessages();
    }

    // 转换回 Prompt 对象的方法
    public Prompt toPrompt() {
        return new Prompt(messages);
    }

    // getter 和 setter
    public List<Message> getMessages() { return messages; }
    public void setMessages(List<Message> messages) { this.messages = messages; }
}
  1. 修改 ChatContextEntity 类,替换 Prompt 为自定义 ChatPrompt
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash("chatContext")
public class ChatContextEntity {
    @Id
    private String userId;
    private ChatPrompt chatPrompt;

    // 无参构造函数、getter、setter 略
}

使用时,保存前将 Prompt 转换为 ChatPrompt,查询后再通过 toPrompt() 方法转回 Prompt 即可。

方案三:改用 JDK 序列化器

若 Prompt 类实现了 Serializable 接口(Spring AI 中的 Prompt 已实现),可切换为 JDK 序列化器,它不需要无参构造函数即可完成反序列化:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();

        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jdkSerializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jdkSerializer);

        template.afterPropertiesSet();
        return template;
    }
}

该方案缺点是序列化后的数据为二进制格式,可读性差,且后续 Prompt 类结构变更时可能出现兼容性问题。

内容的提问来源于stack exchange,提问作者belligol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 22:12:09