[Redis] ** cannot be cast to java.lang.String
先上问题:
java.lang.ClassCastException: com.ppdai.cbd.ddp.thirdparty.contract.bhxtzx.BHXTZXTask cannot be cast to java.lang.String at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:33) at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:117) at org.springframework.data.redis.core.DefaultListOperations.leftPush(DefaultListOperations.java:71) at org.springframework.data.redis.core.DefaultBoundListOperations.leftPush(DefaultBoundListOperations.java:60) at com.ppdai.realtime.datachannel.pullservice.entity.RedisQueue.pushFromHead(RedisQueue.java:63) at com.ppdai.realtime.datachannel.pullservice.redisconfig.RedisTaskSender.sendTask(RedisTaskSender.java:35)at com.ppdai.realtime.datachannel.pullservice.redisconfig.RedisTaskSender$sendTask.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) at com.ppdai.realtime.datachannel.pullservice.dataproviders.bohai.BohaiQueryProvider.afterGetData(BohaiQueryProvider.groovy:395)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210) at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
看问题原因是序列化对象的时候使用的是 StringRedisSerializer, 本来以为是因为 AutoConfig 导致加载了系统默认的 RedisTemplate, 而自己定义了 RedisTemplate 没有加载,
但是非常疑惑的一点是默认的 RedisTemplate 使用的是 JdkSerializationRedisSerializer(如下代码),上面异常报的是使用 redis value 使用 StringRedisSerializer 导致的类型转化失败
public void afterPropertiesSet() {</span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.afterPropertiesSet(); </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> defaultUsed = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">if</span> (defaultSerializer == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { defaultSerializer </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JdkSerializationRedisSerializer( classLoader </span>!= <span style="color: rgba(0, 0, 255, 1)">null</span> ? classLoader : <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.getClass().getClassLoader()); } </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (enableDefaultSerializer) { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (keySerializer == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { keySerializer </span>=<span style="color: rgba(0, 0, 0, 1)"> defaultSerializer; defaultUsed </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (valueSerializer == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { valueSerializer </span>=<span style="color: rgba(0, 0, 0, 1)"> defaultSerializer; defaultUsed </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (hashKeySerializer == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { hashKeySerializer </span>=<span style="color: rgba(0, 0, 0, 1)"> defaultSerializer; defaultUsed </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (hashValueSerializer == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { hashValueSerializer </span>=<span style="color: rgba(0, 0, 0, 1)"> defaultSerializer; defaultUsed </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (enableDefaultSerializer &&<span style="color: rgba(0, 0, 0, 1)"> defaultUsed) { Assert.notNull(defaultSerializer, </span>"default serializer null and not all serializers initialized"<span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (scriptExecutor == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.scriptExecutor = <span style="color: rgba(0, 0, 255, 1)">new</span> DefaultScriptExecutor<K>(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">); } initialized </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; }</span></pre>
最后通过 Alt + F7 (Find Usage) 发现下面这个代码
private Long incr(String key, long delta){ RedisTemplate redisTemplate = getRedisTemplate() ValueOperations<String, String> operations = redisTemplate.opsForValue() redisTemplate.setKeySerializer(new StringRedisSerializer()) redisTemplate.setValueSerializer(new StringRedisSerializer()) return operations.increment(key, delta) }
原来就是这里搞得鬼,将 redisTemplate 的 ValueSerializer 设置成了 StringRedisSerializer ,StringRedisSerializer 参数是 String 类型的,因此就出现了类型转化错误。
附说明
redisTemplate 需要序列化 key,value,hashkey,hashvalue, 提供了很多序列化工具,什么 Jackson、FastJson、JDKSerialization 也可以自己定义。
redisTamplate 默认这四种都使用 JDKSerialization 做序列化,如果没有提供定义默认都用 JDK。