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

Spring3集成Redis Spring Session后Session对象实例不一致问题排查

问题原因分析

这问题我之前帮朋友排查过类似的,核心原因和Spring Session Redis的序列化机制以及你的代码设计有关,咱们一步步拆解:

1. 内存Session vs Redis Session的实例差异

集成前的本地内存Session中,session.getAttribute(USER_ID)返回的是同一个内存引用——对象一直存在内存里,所以两次获取的实例地址完全一致;但集成Redis Session后,Session中的对象会被序列化后存储到Redis,每次调用getAttribute时,都会从Redis读取字节流并反序列化成一个全新的对象实例。这是Redis Session的正常行为,但getName()为空的Bug,问题出在序列化环节的错误配置上。

2. User类的错误继承设计

你的User类继承了JdkSerializationRedisSerializer,这是完全错误的:

  • JdkSerializationRedisSerializer是Spring Data Redis提供的序列化工具类,负责处理对象和字节流的转换,它不是用来让业务POJO继承的基类。
  • 继承这个类会导致User对象在序列化/反序列化过程中,混入工具类的内部状态,触发错误的序列化逻辑,最终导致反序列化后的User对象字段无法正确恢复,比如name属性为空。

3. RedisTemplate的配置不匹配

你用的是StringRedisTemplate,但它默认的序列化器是StringRedisSerializer——这个序列化器只能处理字符串类型的数据,当你存储User对象时,它会把对象强制转成字符串(比如调用toString()),而非完整的二进制序列化,这会导致Redis中存储的是User对象的toString结果,而非完整的序列化字节流,反序列化自然无法正确还原对象属性。

解决方案

针对这些问题,咱们一步步修复:

1. 修复User类的继承结构

把User类的继承关系改成仅实现Serializable接口,去掉JdkSerializationRedisSerializer的继承:

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    // 你的业务字段、getter/setter等
    private String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    // 确保存在默认无参构造函数(Java序列化需要)
    public User() {}
}

确保所有需要序列化的字段没有用transient标记(除非你明确不想序列化该字段)。

2. 调整RedisTemplate配置

把StringRedisTemplate换成通用的RedisTemplate,并配置正确的序列化器:

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
    <!-- 配置键值的序列化器:键用String,值用JDK序列化 -->
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    </property>
    <property name="valueSerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
    </property>
    <!-- 哈希结构的序列化器也建议配置,避免嵌套对象序列化问题 -->
    <property name="hashKeySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    </property>
    <property name="hashValueSerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
    </property>
</bean>

StringRedisTemplate是RedisTemplate的子类,专门用于字符串场景,存储对象必须用通用的RedisTemplate并配置对象序列化器。

3. 手动验证序列化正确性

在本地写个小测试,验证User对象的序列化和反序列化是否能正确保留属性:

public class SerializationTest {
    public static void main(String[] args) {
        JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
        
        User original = new User();
        original.setName("TestName");
        
        // 序列化
        byte[] bytes = serializer.serialize(original);
        // 反序列化
        User deserialized = (User) serializer.deserialize(bytes);
        
        System.out.println("Original name: " + original.getName());
        System.out.println("Deserialized name: " + deserialized.getName());
    }
}

如果反序列化后的name为空,说明User类本身的序列化有问题(比如字段被transient标记,或者缺少无参构造函数)。

额外注意点
  • 确保serialVersionUID固定不变:如果后续修改User类的字段,不要随便修改serialVersionUID,否则会导致Redis中旧的序列化数据无法反序列化。
  • 清理Redis中的旧数据:之前用错误方式存储的User数据已经损坏,需要用redis-cli删除对应的Session键(比如spring:session:*前缀的键),避免旧数据干扰测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:14:25