Redis爬坑——Redis实现通用序列化器 & 解决Redis反序列化失败

Redis默认序列化是 JdkSerializationRedisSerializer,由此可见

public void afterPropertiesSet() {
    super.afterPropertiesSet();
    boolean defaultUsed = false;
    if (this.defaultSerializer == null) {
        this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
    }

    if (this.enableDefaultSerializer) {
        if (this.keySerializer == null) {
            this.keySerializer = this.defaultSerializer;
            defaultUsed = true;
        }

        if (this.valueSerializer == null) {
            this.valueSerializer = this.defaultSerializer;
            defaultUsed = true;
        }

        if (this.hashKeySerializer == null) {
            this.hashKeySerializer = this.defaultSerializer;
            defaultUsed = true;
        }

        if (this.hashValueSerializer == null) {
            this.hashValueSerializer = this.defaultSerializer;
            defaultUsed = true;
        }
    }

    if (this.enableDefaultSerializer && defaultUsed) {
        Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
    }

    if (this.scriptExecutor == null) {
        this.scriptExecutor = new DefaultScriptExecutor(this);
    }

    this.initialized = true;
}

这里因为我们的项目需要更改默认序列策略为Jackson2JsonRedisSerializer让它序列化为可视化的***json***语句

Redis爬坑——Redis实现通用序列化器 & 解决Redis反序列化失败_第1张图片

我们首先定义自己的RedisTemplate,这里我们不要为了每一个类定义一个序列化器,我们定义一个统一的序列化器所以这里泛型是 ,key我们使用StringRedisSerializer,value使用Jackson2JsonRedisSerializer

注释代码为修复反序列化bug的代码

    @Bean
    public RedisTemplate<String, Object> objectRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<String, Object> template = new RedisTemplate();
        Jackson2JsonRedisSerializer<Object> jsonSerial = new 		    Jackson2JsonRedisSerializer(Object.class);
//        //修复反序列化bug
//        ObjectMapper om = new ObjectMapper();
//        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        jsonSerial.setObjectMapper(om);
        template.setDefaultSerializer(jsonSerial);
        template.setKeySerializer(RedisSerializer.string());
        template.setConnectionFactory(redisConnectionFactory);
        template.afterPropertiesSet();
        return template;
    }

测试代码为

@Test
public void redisSaveObject(){

    UserDO ob = new UserDO();
    ob.setName("name");
    ob.setCity("city");
    objectRedisTemplate.opsForValue().set("ob1",ob);
    Object ob2 = objectRedisTemplate.opsForValue().get("ob1");
    UserDO ob1 = (UserDO)ob2;
    System.out.println(ob1);

}

运行结果为

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.hcy.core.model.UserDO
at com.hcy.core.redistest.RedisTest.redisSaveObject(RedisTest.java:42)
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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at 

很明显是对象强制转换错误

这是因为泛型的原因,redis在序列化时候把他当成Object序列化的,所以这里反序列化为Object是可以的,但是因为这个Object没有类型定义所以无法强转。

解决办法

在RedisTemplate中对序列化器Jackson2JsonRedisSerializer进行修改添加如下代码,上文注释了

       //修复反序列化bug
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonSerial.setObjectMapper(om);
  • 通过 objectMapper.enableDefaultTyping() 方法设置

  • 即使使用 Object.class 作为 jcom.fasterxml.jackson.databind.JavaType 也可以实现相应类型的序列化和反序列化

  • 好处:只定义一个序列化器就可以了(通用)

    详细实例了解:https://segmentfault.com/a/1190000009680752

这里我们也做个测试,分别用不修改ObjectMapper的和修改了ObjectMapper的看看生成的value有啥子不一样
Redis爬坑——Redis实现通用序列化器 & 解决Redis反序列化失败_第2张图片

Redis爬坑——Redis实现通用序列化器 & 解决Redis反序列化失败_第3张图片
运行结果:

ob1: [“com.hcy.core.model.UserDO”,{“userid”:null,“openid”:null,“name”:“name”,“city”:“city”}]

ob2: {“userid”:null,“openid”:null,“name”:“name”,“city”:“city”}

这里结果很明显啦!!!

希望对大家有帮助!!!

你可能感兴趣的:(Java学习,bug,SpringBoot)