SpringBoot 调用 RedisTemplate 存储列表 redisTemplate.opsForList().rightPushAll(K var1, V... var2) 报错:xxxUser cannot be cast to java.lang.String
调试过程
通过本地调试,经过一个半天终于确定了是 redis 缓存信息有问题导致了任务调度没有达到预期。前面调试过没有问题的业务逻辑部分忽略,涉及的部分主要在对数据库一个规则表做缓存处理,因为业务需要会频繁使用。
大概问题是这样:刷新缓存的部分出错,导致规则没有写入到 redis,业务消费时找不到 redis 存储的规则,自然不会正常执行。所以关键点在数据写入到 redis。直接调用和调试都会报错。
调用方法:
报错信息:
一开始以为是 opsForList().rightPushAll()
接受到第二个参数需要是 String 类型(从报错信息来看)。API rightPushAll(K var1, V... var2)
,K、V 都是 ListOperations<K, V>
定义的泛型类型。从自动装配注入的 RedisTemplate<String, Object> redisTemplate
可以看出,第二个参数应当为 Object 类型。而 xxxUser 传递进来 upCasting 向上转换 Object 应该是不成问题的。
百度搜索 RedisTemplate<String, Object> redis 使用 opsForList().rightPushAll 放入对象
,可以得到 AI 生成的一大串 demo 使用方法:
不得不赞一句百度 AI 真的很强大,以后一些基础性的对象使用方法,完全可以从它这里参考。
对照存入和读取方法都没有问题。然后又百度了 opsForList 存储对象报错 User cannot be cast to java.lang.String
,这里提供了一个解决方法:
但是读取的时候也要处理:
在写入的部分修改了一下,发现存储的数据处理方式是不一样的。查看 gitlab 这个文件接近一年的时间都没有修改过,所以基本排除了当前 redis 读取代码存在问题的可能性。
回过头来发现,前面百度到的使用 demo 中,有提及到一些信息:
需要注意的是 RedisTemplate 默认使用的序列化器是 JdkSerializationRedisSerializer,它会将对象序列化后存储
如果你需要存储的是简单的字符串或数字,可以使用StringRedisTemplate,它使用 StringRedisSerializer 来序列化字符串。
如果你需要存储复杂对象,可以使用 JSON 序列化器,如 Jackson2JsonRedisSerializer,这样可以存储对象的 JSON 表示。
SpringBoot 使用本来就会有很多配置,正常都会有一个配置文件 xxConfig,是否是在配置文件里面定义了序列化器呢?进入到 RedisTemplate.class
中,按住 command 点击类名,查找使用的地方,找到了一个 RedisConfig.java
文件。
主要关注返回了 redisTemplate
对象的方法,设置了 keySerializer
、hashKeySerializer
使用 StringRedisSerializer
序列化器,同时设置了 hashValueSerializer
、valueSerializer
使用 Jackson2JsonRedisSerializer
序列化器。
从现有配置来看,配置文件一点儿问题都没有,还非常完善。好奇注入进来的 redisTemplate
有没有使用这些序列化器,通过断点调试,果然有问题,四项 keySerializer
、hashKeySerializer
、hashValueSerializer
、valueSerializer
使用的都是 StringRedisSerializer
。添加一项 redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer());
再次查看 defaultSerializer
设置是生效的。然后观察到这些实例对象@Id 值是不一样的,有先后,也就是说不是同一次初始化的。这说明序列化器有重新配置过。
解决
再次查找 RedisTemplate 类的使用里,找到了一个 RedisService 的组件,里面同样注入了 RestTemplate 对象,或者说使用的是同一个实例对象。这个是我从另一个项目里 copy 过来的,主要是从 redis 中读取和存储 token 用户信息。跟一般的使用不同的是,里面还加了一个自动装配的方法:
@Autowired(required=false)
表示忽略当前要注入的 bean 如果有直接注入,没有就跳过,不会报错。所以这个方法也是会自动执行的。里面设置了四项为字符串序列化器,问题就出在这里了。redis 重复配置了。
注释掉对于 redis K、V 序列化器的重新设置,测试检查这部分 redis 操作没有报错,OK,问题解决。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。