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 类指定反序列化时使用的有参构造函数,绕开无参构造的限制:
- 编写自定义 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); } } }
- 配置 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 的核心属性,完全控制序列化/反序列化逻辑,避免依赖第三方类的构造限制:
- 编写自定义 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; } }
- 修改 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
相关产品推荐
相关产品推荐

