在SpringBoot中,最近使用RedisTemplate来对redis进行操作,但是我们会发现,当我们修改了RedisTemplate的序列化方式之后,在存取Long类型数据时,会发生一些问题。
问题:
存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误。java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
示例代码:
@Test
public void redisSerializerLong(){
try {
Long longValue = 123L;
redisLongCache.set("cacheLongValue",longValue);
Object cacheValue = redisLongCache.get("cacheLongValue");
Long a = (Long) cacheValue;
}catch (ClassCastException e){
e.printStackTrace();
}
}
会报类型转换错误java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long。
问题排查:
为什么类型会变为Integer呢?跟我一起追踪源码,便会发现问题。
1. 在代码的最外层获取redis中key对应的value值
redisTemplate.opsForValue().get(key);
2.在DefaultValueOperations类中的get(Object key)方法
public V get(Object key) {
return execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.get(rawKey);
}
}, true);
}
3.打断点继续往里跟,RedisTemplate中的execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline)方法里面,有一行关键代码。
T result = action.doInRedis(connToExpose);
此为获取redis中对应的value值,并对其进行反序列化操作。
4.在抽象类AbstractOperations<K, V>中,定义了反序列化操作,对查询结果result进行反序列化。
public final V doInRedis(RedisConnection connection) {
byte[] result = inRedis(rawKey(key), connection);
return deserializeValue(result);
}
V deserializeValue(byte[] value)反序列化
V deserializeValue(byte[] value) {
if (valueSerializer() == null) {
return (V) value;
}
return (V) valueSerializer().deserialize(value);
}
5.终于到了具体实现类GenericJackson2JsonRedisSerializer
public Object deserialize(@Nullable byte[] source) throws SerializationException {
return deserialize(source, Object.class);
}
实现反序列化方法,注意!这里统一将结果反序列化为Object类型,所以这里便是问题的根源所在,对于数值类型,取出后统一转为Object,导致泛型类型丢失,数值自动转为了Integer类型也就不奇怪了。
那么当读取的时候,代码并不知道缓存中的数字到底是Integer类型还是Long类型,于是乎会使用实际数字的值进行自动转换。
那我们有什么办法可以解决这个问题呢?除了使用Object对象进行缓存结果的承接,然后判断类型进行转换,有没有什么更好的办法?
我们仔细观察一下Integer类和Long类,会发现他们都继承了一个Number类,而且Number类中存在intValue()和longValue() 两个方法。那么问题就迎刃而解了,我们可以直接使用(Number)进行类型转换,在需要使用的地方用具体的xxValue()方法进行值的获取即可。