Spring的RedisTemplate的json反序列泛型丟失問題解決
背景
在使用redisTemplate操作redis時(shí)我們針對對象的序列化通常將序列化成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進(jìn)行配置。但是這種方式會引發(fā)一個(gè)問題當(dāng)進(jìn)行反序列時(shí)如果是對象則會報(bào)錯(cuò)例如: SecurityUserInfo o = (SecurityUserInfo) redisTemplateObject.opsForValue().get(key); 會報(bào)linkedHashMap無法轉(zhuǎn)成具體的類型。因?yàn)樾蛄谢膉son沒有包含類型信息。只能按照默認(rèn)的方式轉(zhuǎn)換成linkedHashMap
解決方案
方案一
將jackson庫的ObjectMapper序列化時(shí)帶上類型信息mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); 但是這種方式會有幾個(gè)缺點(diǎn):
- 增加redis存儲,因?yàn)閹系念愋托畔?/li>
- 可讀性下降,類型信息會混淆在json中
- 如果多個(gè)應(yīng)用讀寫redis會增加理解成本 所以此方案并不推薦
方案二
不使用activateDefaultTyping,查詢的時(shí)候使用Object接收,然后使用mapper.convertValue方法轉(zhuǎn)換。缺點(diǎn)是多一次序列化的操作,影響性能
方案三
曲線救國,使用ScopeValue將類型信息傳遞給RedisTemplate的序列化器(也可以用ThreadLocal),當(dāng)反序列化時(shí)動態(tài)獲取其類型。這個(gè)方式需要增加幾個(gè)類,使用方式變化一下
- 增加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);
}
}
- 調(diào)用方式
SecurityUserInfo securityUserInfo = RedisDeserializeHelper.call(SecurityUserInfo.class, () -> redisTemplateObject.opsForValue().get(key));
總結(jié)
- 如果性能要求不高推薦使用方案二,對性能要求高可以參考方案三
- 另外對于redisson的序列化也會遇到相同的問題,但是redisson可以再從redis獲取值時(shí)指定編解碼器。所以這個(gè)問題影響比較小。
到此這篇關(guān)于Spring的RedisTemplate的json反序列泛型丟失問題解決的文章就介紹到這了,更多相關(guān)RedisTemplate 反序列泛型丟失內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- RedisTemplate默認(rèn)序列化方式顯示中文亂碼的解決
- RedisTemplate序列化設(shè)置的流程和具體步驟
- redis redistemplate序列化對象配置方式
- 配置redis的序列化,注入RedisTemplate方式
- Spring?boot?RedisTemplate?序列化服務(wù)化配置方式
- Springboot下RedisTemplate的兩種序列化方式實(shí)例詳解
- Springboot?引入?Redis?并配置序列化并封裝RedisTemplate?
- Redis之RedisTemplate配置方式(序列和反序列化)
- 解決RedisTemplate的key默認(rèn)序列化器的問題
相關(guān)文章
詳解基于Spring Boot/Spring Session/Redis的分布式Session共享解決方案
本篇文章主要介紹了詳解基于Spring Boot/Spring Session/Redis的分布式Session共享解決方案 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
云計(jì)算實(shí)驗(yàn):Java?MapReduce編程
這篇文章主要介紹了云計(jì)算實(shí)驗(yàn):Java?MapReduce編程,?居于Java圍繞MapReduce編程展開詳細(xì)內(nèi)容,文章助大家掌握MapReduce編程,理解MapReduce原理,需要的朋友可以參考一下2021-12-12
Java關(guān)于遠(yuǎn)程調(diào)試程序教程(以Eclipse為例)
這篇文章主要介紹了Java關(guān)于遠(yuǎn)程調(diào)試程序教程(以Eclipse為例),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
詳談Java中Object類中的方法以及finalize函數(shù)作用
下面小編就為大家?guī)硪黄斦凧ava中Object類中的方法以及finalize函數(shù)作用。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04

