記錄Redis序列化的坑-存Long取Integer的類型轉換錯誤問題及String對象被轉義的問題

背景

最近遇到了兩個Redis相關的問題,趁着清明假期,梳理整理。java

1.存入Long類型對象,在代碼中使用Long類型接收,結果報類型轉換錯誤。redis

2.String對象的反序列化問題,直接在Redis服務器上新增一個key-value,然後在代碼中get(key)時,報反序列化失敗。spring

Long類型接收返回值報錯的問題

Redis的配置以下bash

Redis中序列化相關的配置,我這裏採用的是GenericJackson2JsonRedisSerializer類型的序列化方式(這種方式會有一個類型轉換的坑,下面會提到)服務器

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfiguration {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}&emsp;&emsp;
複製代碼

存入Long對象取出Integer對象

測試方法以下app

@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();
    }
}
複製代碼

long類型轉換

會報類型轉換錯誤java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long。ide

爲何類型會變爲Integer呢?跟我一塊兒追蹤源碼,便會發現問題。測試

1. 在代碼的最外層獲取redis中key對應的value值ui

redisTemplate.opsForValue().get(key);
複製代碼

2.在DefaultValueOperations類中的get(Object key)方法spa

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 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類型也就不奇怪了。

public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws SerializationException {

    Assert.notNull(type,
            "Deserialization type must not be null! Pleaes provide Object.class to make use of Jackson2 default typing.");

    if (SerializationUtils.isEmpty(source)) {
        return null;
    }

    try {
        return mapper.readValue(source, type);
    } catch (Exception ex) {
        throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
    }
}&emsp;&emsp;
複製代碼

String對象轉義問題

測試方法

@Test
public void redisSerializerString() {
    try {
        String stringValue = "abc";
        redisStringCache.set("codeStringValue", stringValue);
        String cacheValue = redisStringCache.get("codeStringValue");
&emsp;&emsp;&emsp;&emsp; // 序列化失敗
        String serverInsert = redisStringCache.get("serverInsertValue");
        if (Objects.equals(cacheValue, serverInsert)) {
            System.out.println("serializer ok");
        } else {
            System.out.println("serializer err");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
複製代碼

提早在redis服務器上插入一個非Json格式的String對象

server ist

直接在Redis服務器上使用set命令新增一對Key-Value,在代碼中取出會反序列化失敗。

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized token 'abc': was expecting ('true', 'false' or 'null')
 at [Source: (byte[])"abc"; line: 1, column: 7]; nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'abc': was expecting ('true', 'false' or 'null')
 at [Source: (byte[])"abc"; line: 1, column: 7]
	at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:132)
	at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:110)
	at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:334)
	at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:60)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95)
	at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:48)&emsp;&emsp;
複製代碼

總結

這個問題是由於,本身在測試的過程當中,沒有按照代碼流程執行,想固然的認爲,代碼跑出來的結果和本身手動插入的結果是同樣的。

在相關的測試驗證過程當中應該嚴格的控制變量,不能憑藉下意識的決斷來操做,謹記軟件之事——必做於細!

相關文章
相關標籤/搜索