Redis缓存序列化问题

2022-08-15 14:25:50

RedisSerializer

RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer

SpringBoot提供的Redis存储序列化方式,常用的有以下几种:

  • JdkSerializationRedisSerializer:将数据序列化为对象;
  • StringRedisSerializer:将数据序列化为字符串;
  • Jackson2JsonRedisSerializer、GenericJackson2JsonRedisSerializer:将数据序列化为json;

JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。
优点是反序列化时不需要提供类型信息(class);
缺点是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。

Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。
优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。

RedisConfig

KeyGenerator

默认使用SimpleKeyGenerator

publicclassSimpleKeyGeneratorimplementsKeyGenerator{//...publicObjectgenerate(Object target,Method method,Object... params){returngenerateKey(params);}publicstaticObjectgenerateKey(Object... params){if(params.length==0){returnSimpleKey.EMPTY;}else{//...returnnewSimpleKey(params);}}}publicclassSimpleKeyimplementsSerializable{publicstaticfinalSimpleKey EMPTY=newSimpleKey(newObject[0]);privatefinalObject[] params;privatetransientint hashCode;//...publicStringtoString(){returnthis.getClass().getSimpleName()+" ["+StringUtils.arrayToCommaDelimitedString(this.params)+"]";}}

从上述代码可见,会将参数params通过SimpleKey组装,生成一长串字符串(类名+[params…])
一般可覆写如下:

importorg.apache.commons.codec.digest.DigestUtils;@Bean@OverridepublicKeyGeneratorkeyGenerator(){return(target, method, params)->{Map<String,Object> container=newHashMap<>(4);Class<?> targetClassClass= target.getClass();// 类地址
            container.put("class", targetClassClass.toGenericString());// 方法名称
            container.put("methodName", method.getName());// 包名称
            container.put("package", targetClassClass.getPackage());// 参数列表for(int i=0; i< params.length; i++){
                container.put(String.valueOf(i), params[i]);}// 转为JSON字符串String jsonString=ObjectMapperUtil.obj2String(container);// 做SHA256 Hash计算,得到一个SHA256摘要作为KeyreturnDigestUtils.sha256Hex(jsonString);};}

ValueSerializer

问题:
从redis取数据将json反序列化为具体pojo时,报错java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.XXX
解决:
在ObjectMapper添加一行
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

原先使用的方法enableDefaultTyping已经过期,有安全漏洞Jackson-databind

@SuppressWarnings("all")@Bean(name="redisTemplate")@ConditionalOnMissingBean(name="redisTemplate")publicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<Object,Object> template=newRedisTemplate<>();// 使用Jackson2JsonRedisSerialize替换默认序列化方式Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer=newJackson2JsonRedisSerializer<>(Object.class);StringRedisSerializer stringRedisSerializer=newStringRedisSerializer();
        jackson2JsonRedisSerializer.setObjectMapper(ObjectMapperUtil.objectMapper);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);// key的序列化采用StringRedisSerializer
        template.setKeySerializer(newStringRedisSerializer());
        template.setHashKeySerializer(newStringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);return template;}

SimpleGrantedAuthority

问题:
Cannot construct instance of 'org.springframework.security.core. authority.SimpleGrantedAuthority'
解决:
在ObjectMapper 添加一行objectMapper.addMixIn(SimpleGrantedAuthority.class,SimpleGrantedAuthorityMixin.class);

详细请看:
实体类字段为接口的json序列化报错的解决方法 以 SpringSecurity UserDetails实现类 GrantedAuthority 为例

  • 作者:m0_60725291
  • 原文链接:https://blog.csdn.net/m0_60725291/article/details/125101220
    更新时间:2022-08-15 14:25:50