[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 &amp;&amp;<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&lt;K&gt;(<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。