您好,我是码农飞哥,感谢您阅读本文!最近在进行框架改造,历史遗留代码对Redis的使用不当,导致了一些问题。

问题描述

org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: com.coep.model.domain.UserCache
	at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84)

从问题的描述我们就可以轻松的定位到这是由于调用JdkSerializationRedisSerializer的deserialize方法对redis值进行反序列化时报的错。

问题分析

错误提示是ClassNotFoundException: com.coep.model.domain.UserCache,这个UserCache类找不到了,为啥找不到呢,这是由于我们在进行框架改造时变更了类的包名。所以,反序列化类时就找不到了。原有的对redis的操作类。

对redis使用的错误示范

 private static RedisSerializer<Object> jdkRedisSerializer = new JdkSerializationRedisSerializer();
    private static RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();

public boolean set(final String key, final Object obj, final long seconds) {
        // key的组织形式 namespace:id:parmeter
        strRedisTemplate.execute((RedisCallback<Serializable>) connection -> {
            final byte[] keyBytes = stringRedisSerializer.serialize(key);
            final byte[] name = jdkRedisSerializer.serialize(obj);
            connection.setEx(keyBytes, seconds, name);
            return null;
        });
        return true;
    }
	
	  @Override
    public Serializable get(final Serializable key) {
        return strRedisTemplate.execute((RedisCallback<Serializable>) connection -> {
            byte[] keyBytes = stringRedisSerializer.serialize( key);
            byte[] bytes = connection.get(keyBytes);
            return (Serializable) jdkRedisSerializer.deserialize(bytes);
        });
    }

这里设置键值时,对key使用的是StringRedisSerializer进行序列化,对value使用的是JdkSerializationRedisSerializer进行序列化。序列化后的数据类型是二进制的字节码文件,可视性差。而且使用JdkSerializationRedisSerializer序列化性能比较差,所以不推荐使用。
在这里插入图片描述

正确的姿势

对于历史的记录,只能在报错之后删除掉该key。这里推荐使用StringRedisSerializer来序列化key和value。相关的配置如下:
使用RedisTemplate操作类的配置如下:

  @Primary
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        try {
            //测试连通性
            factory.getConnection();
        } catch (RedisConnectionFailureException e) {
            LOGGER.warn("未发现redis服务。");
            redisAvailable = false;
        }

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

或者直接使用StringRedisTemplate操作类的配置如下:

  @Bean
    public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(redisConnectionFactory);
        return stringRedisTemplate;
    }

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐