Spring的RedisTemplate的json反序列泛型丢失问题解决

 更新时间:2025年07月18日 11:24:51   作者:村姑飞来了  
本文主要介绍了Spring RedisTemplate中使用JSON序列化时泛型信息丢失的问题及其提出三种解决方案,可以根据性能需求选择方案,下面就来具体了解一下

背景

在使用redisTemplate操作redis时我们针对对象的序列化通常将序列化成json存储到redis。一般如下配置

@Bean  
@ConditionalOnMissingBean  
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory redisConnectionFactory,  
                                         ObjectProvider<RedisTemplateCustomizer> customizers) {  
    RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();  
    redisTemplate.setConnectionFactory(redisConnectionFactory);  
  
    StringRedisSerializer keySerializer = new StringRedisSerializer();  
    redisTemplate.setKeySerializer(keySerializer);  
    redisTemplate.setHashKeySerializer(keySerializer);  
  
    ObjectMapper objectMapper = ObjectMapperWrapper.getObjectMapper();  
    GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);  
    redisTemplate.setValueSerializer(valueSerializer);  
    redisTemplate.setHashValueSerializer(valueSerializer);  
  
    customizers.orderedStream().forEach((customizer) -> customizer.customize(redisTemplate));  
    return redisTemplate;  
}

使用GenericJackson2JsonRedisSerializer进行配置。但是这种方式会引发一个问题当进行反序列时如果是对象则会报错例如: SecurityUserInfo o = (SecurityUserInfo) redisTemplateObject.opsForValue().get(key); 会报linkedHashMap无法转成具体的类型。因为序列化的json没有包含类型信息。只能按照默认的方式转换成linkedHashMap

解决方案

方案一

将jackson库的ObjectMapper序列化时带上类型信息mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); 但是这种方式会有几个缺点:

  • 增加redis存储,因为带上的类型信息
  • 可读性下降,类型信息会混淆在json中
  • 如果多个应用读写redis会增加理解成本 所以此方案并不推荐

方案二

不使用activateDefaultTyping,查询的时候使用Object接收,然后使用mapper.convertValue方法转换。缺点是多一次序列化的操作,影响性能

方案三

曲线救国,使用ScopeValue将类型信息传递给RedisTemplate的序列化器(也可以用ThreadLocal),当反序列化时动态获取其类型。这个方式需要增加几个类,使用方式变化一下

  • 增加helper类
import lombok.extern.slf4j.Slf4j;  
  
import java.util.concurrent.Callable;  
  
/**  
 * @author wxl  
 */@Slf4j  
@SuppressWarnings("all")  
public class RedisDeserializeHelper {  
  
    public static final ScopedValue<Class<?>> TYPE = ScopedValue.newInstance();  
  
    public static <R> R call(Class<R> clazz, Callable<Object> op) {  
        try {  
            Object call = ScopedValue.where(TYPE, clazz).call(op);  
            if (call == null) {  
                return null;  
            }  
            if (clazz.isAssignableFrom(call.getClass())) {  
                return (R) call;  
            }  
            return (R) call;  
        } catch (Exception e) {  
            log.error("redis deserialize failed", e);  
            throw new RuntimeException(e);  
        }  
    }  
  
    public static Class<?> get() {  
        return TYPE.get();  
    }  
}
  • 增加自定义编解码器
import com.fasterxml.jackson.databind.ObjectMapper;  
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;  
import org.springframework.data.redis.serializer.SerializationException;  
  
/**  
 * @author wxl  
 */public class SofastGenericJackson2JsonRedisSerializer extends GenericJackson2JsonRedisSerializer {  
  
    public SofastGenericJackson2JsonRedisSerializer(ObjectMapper objectMapper) {  
        super(objectMapper);  
    }  
  
    @Override  
    public Object deserialize(byte[] source) throws SerializationException {  
        Class<?> clazz = RedisDeserializeHelper.get();  
        if (clazz != null) {  
            return deserialize(source, clazz);  
        }  
        return super.deserialize(source);  
    }  
  
    @Override  
    public <T> T deserialize(byte[] source, Class<T> type) throws SerializationException {  
        return super.deserialize(source, type);  
    }  
}
  • 调用方式
SecurityUserInfo securityUserInfo = RedisDeserializeHelper.call(SecurityUserInfo.class, () -> redisTemplateObject.opsForValue().get(key));

总结

  • 如果性能要求不高推荐使用方案二,对性能要求高可以参考方案三
  • 另外对于redisson的序列化也会遇到相同的问题,但是redisson可以再从redis获取值时指定编解码器。所以这个问题影响比较小。

 到此这篇关于Spring的RedisTemplate的json反序列泛型丢失问题解决的文章就介绍到这了,更多相关RedisTemplate 反序列泛型丢失内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解基于Spring Boot/Spring Session/Redis的分布式Session共享解决方案

    详解基于Spring Boot/Spring Session/Redis的分布式Session共享解决方案

    本篇文章主要介绍了详解基于Spring Boot/Spring Session/Redis的分布式Session共享解决方案 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • 详解JAVA 函数式编程

    详解JAVA 函数式编程

    这篇文章主要介绍了JAVA 函数式编程的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • 云计算实验:Java MapReduce编程

    云计算实验:Java MapReduce编程

    这篇文章主要介绍了云计算实验:Java MapReduce编程, 居于Java围绕MapReduce编程展开详细内容,文章助大家掌握MapReduce编程,理解MapReduce原理,需要的朋友可以参考一下
    2021-12-12
  • java数组复制的四种方法效率对比

    java数组复制的四种方法效率对比

    这篇文章主要介绍了java数组复制的四种方法效率对比,文中有简单的代码示例,以及效率的比较结果,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Java关于远程调试程序教程(以Eclipse为例)

    Java关于远程调试程序教程(以Eclipse为例)

    这篇文章主要介绍了Java关于远程调试程序教程(以Eclipse为例),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • MyBatis新增数据并返回主键值方式

    MyBatis新增数据并返回主键值方式

    这篇文章主要介绍了MyBatis新增数据并返回主键值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Java处理延时任务的常用几种解决方案

    Java处理延时任务的常用几种解决方案

    本文主要介绍了Java处理延时任务的常用几种解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • mybatis中的if-test判断解读

    mybatis中的if-test判断解读

    在使用MyBatis进行条件判断时,如果条件中涉及到字符与数字的比较,需要特别注意比较方式,例如,在<if>标签中,比较数字“1”时,应将其写在双引号中,或者使用.toString()方法,避免直接使用字符'1'进行比较
    2024-11-11
  • 详谈Java中Object类中的方法以及finalize函数作用

    详谈Java中Object类中的方法以及finalize函数作用

    下面小编就为大家带来一篇详谈Java中Object类中的方法以及finalize函数作用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • java中深复制知识点详解

    java中深复制知识点详解

    在本篇文章里小编给大家整理了关于java中深复制知识点详解内容,有需要的朋友们可以学习下。
    2020-12-12

最新评论