RedisTemplate 的泛型和序列化

1.通常用法

@Resource
RedisTemplate<String, String> redisTemplate;

public void doTest(){
  redisTemplate.opsForValue().setIfAbset("key", "val", Duration.ofSecend(100));
}

RedisTemplate<String, String> 表示操作的 key 和 val 都是String類型。

如果 val 是 Integer

@Resource
RedisTemplate<String, Integer> redisTemplate;

public void doTest(){
  redisTemplate.opsForValue().setIfAbset("key", 100, Duration.ofSecend(100));
}

這個(gè)就可能報(bào)錯(cuò)了 這是因?yàn)閟pringboot訪問redis時(shí)的序列化操作。

2.Serializer

springboot與Redis的交互是以二進(jìn)制方式進(jìn)行(byte[])。為了支持Java中的數(shù)據(jù)類型,就要對(duì)操作的對(duì)象(key,value,hashKey,hashValue...)做序列化操作。

redisTemplate 只為 key value hashKey hashValue 設(shè)置serializer

Springboot 提供了幾個(gè)序列化的方法。

  • JdkSerializationRedisSerializer 默認(rèn)
  • StringRedisSerializer
  • 其他 或 自定義

JdkSerializationRedisSerializer的序列化操作函數(shù)簽名如下

public byte[] serialize(@Nullable Object object)

StringRedisSerializer序列化操作函數(shù)簽名如下

public byte[] serialize(@Nullable String string)

因?yàn)?StringRedisSerializer 只支持 string類型 所以如果使用RedisTemplate<String, Integer>就會(huì)報(bào)錯(cuò)。

如果使用 JdkSerializationRedisSerializer 則不僅支持 RedisTemplate<String, Integer>,同樣能支持任意自定義類型 RedisTemplate<String, Person>。

然而這種默認(rèn)的序列化方式會(huì)導(dǎo)致redis中保存的key和value可讀性較差,出現(xiàn)一些不可讀的16進(jìn)制字符

\xAC\xED\00\0x5t\x00

自定義序列化

JdkSerializationRedisSerializer雖然在redis中保存的數(shù)據(jù)是不可讀的,但是操作起來很方便。可直接指定返回值的類型,免去了再次轉(zhuǎn)換之繁瑣。

其實(shí)現(xiàn)原理是在redis中存儲(chǔ)的數(shù)據(jù)里包含著數(shù)據(jù)類型。

有沒有一種可能:在redis中數(shù)據(jù)不保存類型信息,通過為template指定value的類型,獲取期望類型值

即:使用StringRedisSerializer,但能達(dá)到JdkSerializationRedisSerializer的效果。

比如:

@Resource
RedisTemplate<String,Integer> tplInt;
@Resource
RedisTemplate<String,Person> tplPerson;
public void testGet(){
  Integer x = tplInt.get("valOfX");
  Person p = tplPerson.get("valOfPerson");
}

難點(diǎn)在于:get()內(nèi)部是否知道外部期望的類型是什么?

答案是:沒有辦法通過代碼傳入,只能從獲返回的數(shù)據(jù)中獲取。 原因如下:

redis返回的是 byte[] 類型,要用一個(gè) serializer 做反序列化。

RedisTemplate中的 serializer 得是一個(gè)bean,即是一個(gè)實(shí)例化的對(duì)象。這個(gè)對(duì)象要實(shí)現(xiàn) RedisSerializer<T> 接口,因此必定要綁定在一個(gè)固定類型上,如果是String就不能是Integer。所以無法根據(jù)需要傳入。

  • StringRedisSerializer 實(shí)現(xiàn)的是 RedisSerializer<String>
  • JdkSerializationRedisSerializer 實(shí)現(xiàn)的是 RedisSerializer<Object>

后者為了兼容所有類型,所以設(shè)置為Object,反序列化后的數(shù)據(jù)是一個(gè)Object,這樣就丟掉了原本的所有信息。所以如果要返回外部需要的類型,只能在序列化后做一次值的類型轉(zhuǎn)換。本質(zhì)邏輯類似如下:

public Object get(String key){
  //get data from redis
  return (Object)(new Person())
}
Person p = (Person)get();

如果為某個(gè)操作設(shè)置了專門的serializer,由于 template是單例的,其他線程也會(huì)受到影響。

其他坑

  • 除了 StringRedisSerializer ,其他對(duì)字符串0的處理,會(huì)導(dǎo)致redis的incr類指令不可用。(int 0 正常)

3.結(jié)論

選擇1

RedisTemplate<String,String>將就用

選擇2

springboot的序列化可以自定義,自己搭配。比如

//默認(rèn)改成 StringRedisSerializer,key類的原樣
template.setDefaultSerializer(new StringRedisSerializer());
//為value支持復(fù)雜類型
JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();
template.setValueSerializer(jdkSerializer);
template.afterPropertiesSet();

這樣 key、hashKey、hashValue可讀的。value來支持復(fù)雜類型

4.參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容