RedisTemplate默认使用的是JdkSerializationRedisSerializer
,StringRedisTemplate默认使用的是StringRedisSerializer
。
SpringBoot提供的Redis存储序列化方式,常用的有以下几种:
JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。
优点是反序列化时不需要提供类型信息(class);
缺点是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。
Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。
优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。
默认使用SimpleKeyGenerator
public class SimpleKeyGenerator implements KeyGenerator {
//...
public Object generate(Object target, Method method, Object... params) {
return generateKey(params);
}
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
} else {
//...
return new SimpleKey(params);
}
}
}
public class SimpleKey implements Serializable {
public static final SimpleKey EMPTY = new SimpleKey(new Object[0]);
private final Object[] params;
private transient int hashCode;
//...
public String toString() {
return this.getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
}
}
从上述代码可见,会将参数params
通过SimpleKey
组装,生成一长串字符串(类名+[params…])
一般可覆写如下:
import org.apache.commons.codec.digest.DigestUtils;
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
Map<String, Object> container = new HashMap<>(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摘要作为Key
return DigestUtils.sha256Hex(jsonString);
};
}
问题:
从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")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 使用Jackson2JsonRedisSerialize替换默认序列化方式
Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
jackson2JsonRedisSerializer.setObjectMapper(ObjectMapperUtil.objectMapper);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
问题:
Cannot construct instance of 'org.springframework.security.core. authority.SimpleGrantedAuthority'
解决:
在ObjectMapper 添加一行objectMapper.addMixIn(SimpleGrantedAuthority.class,SimpleGrantedAuthorityMixin.class);
详细请看:
实体类字段为接口的json序列化报错的解决方法 以 SpringSecurity UserDetails实现类 GrantedAuthority 为例