Spring Boot向Redis存储数据时出现StackOverflowError求助
Spring Boot Redis保存时StackOverflowError排查与解决建议
来帮你拆解这个Spring Boot存Redis时触发的StackOverflowError问题!从你贴的代码和堆栈信息来看,核心问题肯定是序列化时的无限递归,大概率和对象的循环引用有关,下面是具体的排查思路和解决办法:
问题核心分析
从异常堆栈能看到,MappingRedisConverter在反复调用writeInternal和writeCollection,这明显是序列化过程中陷入了无限循环。结合你的RedisObj里有MyObject field9这个嵌套对象,几乎可以确定:MyObject内部要么引用了RedisObj,要么存在自引用(比如List<MyObject>类型的字段),导致Spring Data Redis在转换对象时不断递归处理,最终撑爆栈内存。
你的相关代码
RedisRepo接口
public interface RedisRepo extends CrudRepository<RedisEntity,String> { }
RedisObj实体类
public class RedisObj { @Id private Long field1; private String field2; private String field3; private String field4; private String field5; private UUID field6; private String field7; private String field8; private MyObject field9; }
业务层调用代码
redisRepo.save(ObjToRedis);
关键异常堆栈片段
Caused by: java.lang.StackOverflowError: null at org.springframework.core.convert.TypeDescriptor.getElementTypeDescriptor(TypeDescriptor.java:331) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.core.convert.TypeDescriptor.equals(TypeDescriptor.java:470) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE] ... at org.springframework.data.redis.core.convert.MappingRedisConverter.writeInternal(MappingRedisConverter.java:583) ~[spring-data-redis-2.1.5.RELEASE.jar:2.1.5.RELEASE] at org.springframework.data.redis.core.convert.MappingRedisConverter.writeCollection(MappingRedisConverter.java:719) ~[spring-data-redis-2.1.5.RELEASE.jar:2.1.5.RELEASE]
排查步骤
- 检查
MyObject的结构:重点看MyObject是否有引用RedisObj的字段,或者存在自引用(比如private MyObject parent;或private List<MyObject> children;),这是递归的常见源头。 - 验证对象转换逻辑:确认
ObjToRedis是如何从业务对象转换而来的,转换过程中是否不小心创建了循环引用的结构。 - 开启调试日志:在配置文件中添加日志配置,打印序列化的细节,精准定位递归的起点:
logging.level.org.springframework.data.redis.core.convert=DEBUG
解决方案
根据不同的递归原因,有几种针对性的处理方式:
1. 直接打破循环引用
如果MyObject里确实有循环引用的字段,可以用注解告诉序列化工具忽略该字段:
public class MyObject { // 忽略循环引用的字段,避免递归序列化 @JsonIgnore private RedisObj redisObj; // 其他字段... }
或者用Spring Data Redis专属的注解:@RedisHash(ignore = true)
2. 配置序列化工具处理循环引用
如果需要保留引用关系,可以配置Jackson序列化器支持循环引用:
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper mapper = new ObjectMapper(); // 开启循环引用检测,用对象ID标记避免递归 mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); mapper.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false); serializer.setObjectMapper(mapper); template.setValueSerializer(serializer); template.setKeySerializer(new StringRedisSerializer()); return template; }
同时在循环引用的类上添加标记注解:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "field1") public class RedisObj { // 字段... }
3. 简化Redis存储对象
如果不需要嵌套对象的完整结构,可以拆分数据:只存储Redis业务需要的字段,把MyObject的必要属性扁平到RedisObj中,或者单独存储MyObject,用键关联的方式引用,避免复杂嵌套带来的序列化问题。
内容的提问来源于stack exchange,提问作者ging
相关产品推荐
相关产品推荐

