背景

最近遇到了两个Redis相关的问题,趁着清明假期,梳理整理。

1.存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误。

2.String对象的反序列化问题,直接在Redis服务器上新增一个key-value,而后在代码中get(key)时,报反序列化失败。

关于Long类型转换错误

Redis的配置如下

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;

存入Long对象取出Integer对象

测试方法如下

@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();

image

会报类型转换错误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类型也就不奇怪了。

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);

String对象转义问题

测试方法

@Test
public void redisSerializerString() {
    try {
        String stringValue = "abc";
        redisStringCache.set("codeStringValue", stringValue);
        String cacheValue = redisStringCache.get("codeStringValue");     // 序列化失败
        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对象

image

直接在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)  

小总结

这个问题是因为,自己在测试的过程中,没有按照代码流程执行,想当然的认为,代码跑出来的结果和自己手动插入的结果是一样的。

在相关的测试验证过程中应该严格的控制变量,不能凭借下意识的决断来操作,谨记软件之事——必作于细!

参考资料:

https://blog.csdn.net/f641385712/article/details/84679456


Spring上传文件报错the request was rejected because its size (15920203) exceeds the configured maximum (104... 在线时间戳转换工具的坑-同样的时间戳转为北京时间,转换结果受本机时区设置的影响... 使用EasyExcel导入excel中的日期格式数据时获取到的却是一个数字 面试官:你觉得Redis存储对象信息是用Hash还是String好?
Redis 内部使用一个 RedisObject 对象来表示所有的 key 和 value,RedisObject 中的 type,则是代表一个 value 对象具体是何种数据类型,它包含字符串(String)、链表(List)、哈希结构(Hash)、集合(Set)、有序集合(Sorted set)。
Map<Integer,Value>放入缓存后取出来变成了Map<String,Value>
Map<Integer,Value>放入缓存后取出来变成了Map<String,Value>
因在缓存对象中增加字段,而导致Redis中取出缓存转化成Java对象时出现反序列化失败的问题...
因在缓存对象中增加字段,而导致Redis中取出缓存转化成Java对象时出现反序列化失败的问题...
Android TextView.setText() 实现字符串(String)+数值(Integer)作为参数
Android TextView.setText() 实现字符串(String)+数值(Integer)作为参数
【SpringBoot】34、SpringBoot整合Redis实现序列化存储Java对象
前面我们已经介绍过【SpringBoot】十七、SpringBoot 中整合 Redis,我们可以看出,在 SpringBoot 对 Redis 做了一系列的自动装配,使用还是非常方便的
Spring上传文件报错the request was rejected because its size (15920203) exceeds the configured maximum (104... 在线时间戳转换工具的坑-同样的时间戳转为北京时间,转换结果受本机时区设置的影响... 使用EasyExcel导入excel中的日期格式数据时获取到的却是一个数字
 
推荐文章