You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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,那可以这么解决:

  1. 解决动态代理序列化问题:让你的实体类实现Serializable,并添加writeReplace()方法,确保序列化的是原对象而非代理对象:
public class YourEntity implements Serializable {
    private transient String yourTransientField;

    // 告诉序列化框架用原对象代替代理对象
    private Object writeReplace() throws ObjectStreamException {
        return this;
    }
}
  1. 自定义序列化逻辑:重写writeObject()方法,手动跳过transient字段:
private void writeObject(ObjectOutputStream out) throws IOException {
    // 调用默认序列化,但会自动跳过transient字段(如果没有代理问题的话)
    out.defaultWriteObject();
    // 这里可以手动控制哪些字段被序列化,不需要的就不写
}

最后再提个小坑

如果你的transient字段是被Spring注入的(比如@Autowired的依赖),代理对象会把这些注入的字段也序列化,这时候上面的writeReplace()方法就能完美解决。

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

火山引擎 最新活动