用 redistemplate + listoperations 是最稳妥方案,但必须显式配置 genericjackson2jsonredisserializer 序列化器,否则因默认 jdk 序列化导致取值为 null 或乱码;key 用 stringredisserializer,value 用 genericjackson2jsonredisserializer,并注意 localdatetime 等类型需注册 javatimemodule。

直接结论:用 RedisTemplate + ListOperations 是最稳妥的方案,但必须显式配置序列化器,否则取出来的 List 会是乱码或 null。
为什么默认的 RedisTemplate<string object></string> 取不到 List 数据?
Spring Boot 的 RedisTemplate 默认使用 JDK 序列化(JdkSerializationRedisSerializer),它把整个 List 对象当做一个整体序列化成二进制字节。你存进去的是 [{"id":"1","name":"a"},{"id":"2","name":"b"}],Redis 里实际存的是类似 aced00057372...(一长串十六进制) 的内容。
这时候如果用 range(key, 0, -1),Redis 返回的是一个个被序列化的 Object 元素,但反序列化失败——因为每个元素不是独立 JSON 字符串,而是嵌套在 List 容器里的完整对象字节流。
常见错误现象:
- range() 返回空 List 或 null
- 控制台打印出类似 java.util.ArrayList@3a5e7876 的 toString 结果
- Redis CLI 中用 LRANGE myKey 0 -1 看到一堆不可读字符
怎么正确配置 RedisTemplate 支持 List 存取?
核心是让 key 和 value 都走 JSON 序列化,且确保每个 list 元素单独序列化(而不是整个 list 一起序列化)。推荐用 GenericJackson2JsonRedisSerializer:
- 不要用
StringRedisTemplate—— 它只支持String类型,没法直接存List<user></user> - 不要用
RedisTemplate<string string></string>存对象 —— 会报ClassCastException,因为反序列化时类型不匹配 - 必须指定 value 序列化器为
GenericJackson2JsonRedisSerializer,并传入具体泛型类型(如User.class)或留空让它自动推断 - key 序列化器建议用
StringRedisSerializer,避免 key 也变成乱码
示例配置:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
@Bean
public RedisTemplate<string object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<string object> template = new RedisTemplate();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}</string></string>
leftPushAll 和 rightPushAll 的实际效果差异
这两个方法本质都是批量 push,区别仅在于插入顺序和语义:
-
leftPushAll(key, list):把list中每个元素依次调用LPUSH,结果是list[0]成为 Redis list 的 头元素(最左),list[n]成为尾元素 -
rightPushAll(key, list):对应RPUSH,list[0]成为 尾元素(最右),list[n]成为头元素 - 性能上无明显差别,但如果你依赖「先进先出」或「后进先出」语义,选错会导致业务逻辑翻转
比如缓存用户最近操作日志,想按时间正序展示(最早 → 最新),就该用 rightPush 单个追加;若用 rightPushAll 批量写入一个已排序的 List<log></log>,顺序会被反转。
取值时 range(key, 0, -1) 的边界陷阱
range 是安全的,但容易忽略两个细节:
-
start和end是 索引位置,不是元素个数。例如range(key, 0, 9)取前 10 个,range(key, 0, -1)取全部 - 如果 key 不存在,
range返回空List(不是null),但如果你没判空直接调用.size()没问题;可一旦中间有元素是null(比如序列化失败导致某次 push 写入了 null 字节),range仍返回包含null元素的List,后续遍历时可能触发NullPointerException - 大 List 场景下慎用
range(key, 0, -1):Redis 会把整个 list 加载进内存再切片,可能引发 OOM。应优先用分页方式(如range(key, offset, offset+limit-1))或改用lrange命令配合游标
真正容易被忽略的是:当你用 GenericJackson2JsonRedisSerializer 存 List<user></user>,但某个 User 字段含 Java 8 时间类型(如 LocalDateTime),而 Jackson 没配 JavaTimeModule,反序列化时会静默失败,对应位置返回 null —— 这种问题不会报错,只会让你在业务层莫名其妙拿到空对象。










