????????RedisAtomicInteger是springdata中在redis的基礎(chǔ)上實(shí)現(xiàn)的原子計數(shù)器,在以下maven依賴包中:
<groupId>org.springframework.data</groupId> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?<artifactId>spring-data-redis</artifactId>
?? ?????當(dāng)使用RedisAtomicInteger(String redisCounter, RedisOperations template,...)函數(shù)構(gòu)建實(shí)例的情況下,在使用INCR或者DECR時髓废,會遇到ERR value is not an integer or out of range錯誤,顯示操作的數(shù)據(jù)不是一個整數(shù)或者超出范圍。參考redis命令說明我們知道incr對操作值的要求:
????????這是一個針對字符串的操作怀跛,因?yàn)?Redis 沒有專用的整數(shù)類型,所以 key 內(nèi)儲存的字符串被解釋為十進(jìn)制 64 位有符號整數(shù)來執(zhí)行 INCR 操作柄冲。如果值包含錯誤的類型吻谋,或字符串類型的值不能表示為數(shù)字,那么返回一個錯誤现横。?
????????從redis實(shí)例中查看該key的value,會發(fā)現(xiàn)結(jié)果類似這樣:"\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x01",原因在于value使用的序列化方式是JdkSerializationRedisSerializer,這和INCR命令對結(jié)果的要求是違背的漓拾。
????????該使用哪種序列化方式把value放進(jìn)去呢?按照INCR命令對結(jié)果的要求戒祠,最容易想到StringRedisSerializer骇两,但經(jīng)過嘗試這也行不通,會報java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String得哆。
????????如果看過RedisAtomicInteger的源碼脯颜,在private RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory, Integer initialValue)中會發(fā)現(xiàn)方法內(nèi)部創(chuàng)建了RedisTemplate實(shí)例哟旗,對value設(shè)置的序列化方式是GenericToStringSerializer贩据。該序列化內(nèi)部使用spring core包下的org.springframework.core.convert.support.DefaultConversionService作為默認(rèn)的對象和字符串的轉(zhuǎn)換方式,主要為了滿足大多數(shù)環(huán)境的要求闸餐。
????????至此饱亮,我們終于知道了錯誤的根本原因,構(gòu)造RedisAtomicInteger時傳入的redisTemplate是有問題的舍沙,value的默認(rèn)序列化方式不滿足RedisAtomicInteger的需要近上。那么問題也迎刃而解,將GenericToStringSerializer作為redisTemplate的value序列化方式拂铡。
? ? ? ? ?這樣雖然解決了問題壹无,但很麻煩,很可能為了RedisAtomicInteger的要求需要再創(chuàng)建一個redisTemplate感帅,簡直不能忍受斗锭。再看RedisAtomicInteger的源碼,發(fā)現(xiàn)構(gòu)造函數(shù)除了可以用redisTemplate,還可以用RedisConnectionFactory失球,嘗試之后岖是,完美解決。