Spring Data与Redis:瞬态字段意外序列化问题解决咨询
解决Spring Data Redis中瞬态字段被JDK序列化的问题
嘿,我刚好碰到过类似的问题,咱们一步步来拆解解决:
先搞清楚为啥会这样
首先你得明确:JdkSerializationRedisSerializer用的是Java原生序列化机制,所以:
@JsonIgnore完全没用——这个注解是给Jackson JSON序列化用的,和JDK原生序列化不搭边;- 理论上
transient关键字应该能阻止字段被序列化,但没生效大概率是因为你的对象被Spring生成了动态代理(比如加了@Transactional、AOP切面的类),CGLIB代理生成的子类会继承原类的字段,序列化代理对象时会把transient字段也带进去,或者你的对象有自定义序列化逻辑不小心处理了这些字段。
最推荐的解决方案:换用Jackson序列化器
放弃JDK原生序列化,改用Jackson2JsonRedisSerializer是最优解——序列化结果是可读的JSON,支持@JsonIgnore,还能避免JDK序列化的各种坑(比如版本兼容、序列化体积大)。
配置方式很简单,写个Redis配置类:
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // 初始化Jackson序列化器 Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); // 配置ObjectMapper,支持类型信息(反序列化时能识别对象类型) objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); // 让Jackson也识别transient关键字(可选,如果你想保留transient的话) objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true); jacksonSerializer.setObjectMapper(objectMapper); // 设置value和hashValue的序列化器 template.setValueSerializer(jacksonSerializer); template.setHashValueSerializer(jacksonSerializer); // key用字符串序列化器(避免乱码) template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; } }
配置完之后,你给不想序列化的字段加@JsonIgnore就生效了;如果习惯用transient,上面配置里的PROPAGATE_TRANSIENT_MARKER会让Jackson也忽略带transient的字段。
如果你非要用JDK原生序列化
如果因为某些原因必须保留JdkSerializationRedisSerializer,那可以这么解决:
- 解决动态代理序列化问题:让你的实体类实现
Serializable,并添加writeReplace()方法,确保序列化的是原对象而非代理对象:
public class YourEntity implements Serializable { private transient String yourTransientField; // 告诉序列化框架用原对象代替代理对象 private Object writeReplace() throws ObjectStreamException { return this; } }
- 自定义序列化逻辑:重写
writeObject()方法,手动跳过transient字段:
private void writeObject(ObjectOutputStream out) throws IOException { // 调用默认序列化,但会自动跳过transient字段(如果没有代理问题的话) out.defaultWriteObject(); // 这里可以手动控制哪些字段被序列化,不需要的就不写 }
最后再提个小坑
如果你的transient字段是被Spring注入的(比如@Autowired的依赖),代理对象会把这些注入的字段也序列化,这时候上面的writeReplace()方法就能完美解决。
内容的提问来源于stack exchange,提问作者Matt




