用 RedisTemplate + ListOperations 是最稳妥方案,但必须显式配置 GenericJackson2JsonRedisSerializer 序列化器,否则因默认 JDK 序列化导致取值为 null 或乱码;key 用 StringRedisSerializer,value 用 GenericJackson2JsonRedisSerializer,并注意 LocalDateTime 等类型需注册 JavaTimeModule。
直接结论:用 RedisTemplate + ListOperations 是最稳妥的方案,但必须显式配置序列化器,否则取出来的 List 会是乱码或 null。
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 看到一堆不可读字符
核心是让 key 和 value 都走 JSON 序列化,且确保每个 list 元素单独序列化(而不是整个 list 一起序列化)。推荐用 GenericJackson2JsonRedisSerializer:
StringRedisTemplate —— 它只支持 String 类型,没法直接存 List<User>
RedisTemplate<String, String> 存对象 —— 会报 ClassCastException,因为反序列化时类型不匹配GenericJackson2JsonRedisSerializer,并传入具体泛型类型(如 User.class)或留空让它自动推断StringRedisSerializer,避免 key 也变成乱码示例配置:
@Beanpublic 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;}
这两个方法本质都是批量 push,区别仅在于插入顺序和语义:
leftPushAll(key, list):把 list 中每个元素依次调用 LPUSH,结果是 list[0] 成为 Redis list 的 头元素(最左),list[n] 成为尾元素rightPushAll(key, list):对应 RPUSH,list[0] 成为 尾元素(最右),list[n] 成为头元素比如缓存用户最近操作日志,想按时间正序展示(最早 → 最新),就该用 rightPush 单个追加;若用 rightPushAll 批量写入一个已排序的 List<Log>,顺序会被反转。
range 是安全的,但容易忽略两个细节:
start 和 end 是 索引位置,不是元素个数。例如 range(key, 0, 9) 取前 10 个,range(key, 0, -1) 取全部range 返回空 List(不是 null),但如果你没判空直接调用 .size() 没问题;可一旦中间有元素是 null(比如序列化失败导致某次 push 写入了 null 字节),range 仍返回包含 null 元素的 List,后续遍历时可能触发 NullPointerException
range(key, 0, -1):Redis 会把整个 list 加载进内存再切片,可能引发 OOM。应优先用分页方式(如 range(key, offset, offset+limit-1))或改用 lrange 命令配合游标真正容易被忽略的是:当你用 GenericJackson2JsonRedisSerializer 存 List<User>,但某个 User 字段含 Java 8 时间类型(如 LocalDateTime),而 Jackson 没配 JavaTimeModule,反序列化时会静默失败,对应位置返回 null —— 这种问题不会报错,只会让你在业务层莫名其妙拿到空对象。
Tplink企业版路由器WiFi名称的默认设置介绍(Tplink企业版路由器WiFi名称的默认设置是什么)
Tplink路由器灯常亮无法上网的原因分析(如何解决Tplink路由器灯常亮无法上网的问题)
Tplink千兆企业级路由器自动重启的作用和优势介绍(如何设置Tplink千兆企业级路由器自动重启功能)
一根天线的tplink路由器有哪些(一根天线的Tplink路由器的特点和优势介绍)
tplink路由器外网访问不了nas(Tplink路由器外网访问NAS的原因分析)
Tplink无法搜到路由器的原因分析(如何解决Tplink无法搜到路由器的问题)