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集成Shiro并利用MongoDB做Session存储的方法详解

    Spring Boot集成Shiro并利用MongoDB做Session存储的方法详解

    这篇文章主要给大家介绍了关于Spring Boot集成Shiro并利用MongoDB做Session存储的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友下面来一起看看吧。
    2017-12-12
  • Java Clone深拷贝与浅拷贝的两种实现方法

    Java Clone深拷贝与浅拷贝的两种实现方法

    今天小编就为大家分享一篇关于Java Clone深拷贝与浅拷贝的两种实现方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • SpringFramework应用接入Apollo配置中心过程解析

    SpringFramework应用接入Apollo配置中心过程解析

    这篇文章主要介绍了SpringFramework应用接入Apollo配置中心过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Spring 框架实现账户转账功能(推荐)

    Spring 框架实现账户转账功能(推荐)

    通过本文的介绍,我们了解了如何使用Spring框架实现一个简单的账户转账功能,主要使用了 Spring 的依赖注入、和事务管理功能,保证了转账操作的原子性和数据的一致性,感兴趣的朋友跟随小编一起看看吧
    2025-07-07
  • Java设计模式之Prototype原型模式

    Java设计模式之Prototype原型模式

    这篇文章主要为大家详细介绍了Java设计模式之Prototype原型模式的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Spring使用Setter完成依赖注入方式

    Spring使用Setter完成依赖注入方式

    这篇文章主要介绍了Spring使用Setter完成依赖注入方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • java 解决异常 2 字节的 UTF-8 序列的字节2 无效的问题

    java 解决异常 2 字节的 UTF-8 序列的字节2 无效的问题

    这篇文章主要介绍了java 解决异常 2 字节的 UTF-8 序列的字节 2 无效的问题的相关资料,需要的朋友可以参考下
    2016-12-12
  • java、python、JavaScript以及jquery循环语句的区别

    java、python、JavaScript以及jquery循环语句的区别

    本篇文章主要介绍java、python、JavaScript以及jquery的循环语句的区别,这里整理了它们循环语句语法跟示例,以便大家阅读,更好的区分它们的不同
    2016-07-07
  • Java使用Spire.Doc for Java实现Word自动化插入图片

    Java使用Spire.Doc for Java实现Word自动化插入图片

    在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来看看如何使用Spire.Doc for Java实现Word文档自动化插入图片吧
    2025-12-12
  • Java实现定时器的四种方式

    Java实现定时器的四种方式

    这篇文章主要介绍了Java实现定时器的四种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07

最新评论