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 为例