深入理解 Redis Template及4种序列化方式__spring boot整合redis实现RedisTemplate三分钟快速入门
概述
使用Spring 提供的 Spring Data Redis 操作redis 必然要使用Spring提供的模板類 RedisTemplate, 今天我們好好的看看這個模板類 。
RedisTemplate
看看4個序列化相關的屬性 ,主要是 用于 KEY 和 VALUE 的序列化 。 舉個例子,比如說我們經常會將POJO 對象存儲到 Redis 中,一般情況下會使用 JSON 方式序列化成字符串,存儲到 Redis 中 。
Spring提供的Redis數據結構的操作類
- ValueOperations 類,提供 Redis String API 操作
- ListOperations 類,提供 Redis List API 操作
- SetOperations 類,提供 Redis Set API 操作
- ZSetOperations 類,提供 Redis ZSet(Sorted Set) API 操作
- GeoOperations 類,提供 Redis Geo API 操作
- HyperLogLogOperations 類,提供 Redis HyperLogLog API 操作
StringRedisTemplate
再看個常用的 StringRedisTemplate
RedisTemplate<K, V> 支持泛型,StringRedisTemplate K V 均為String類型。
org.springframework.data.redis.core.StringRedisTemplate 繼承 RedisTemplate 類,使用 org.springframework.data.redis.serializer.StringRedisSerializer 字符串序列化方式。
RedisSerializer 序列化 接口
RedisSerializer接口 是 Redis 序列化接口,用于 Redis KEY 和 VALUE 的序列化
RedisSerializer 接口的實現類 如下
歸類一下
- JDK 序列化方式 (默認)
- String 序列化方式J
- SON 序列化方式
- XML 序列化方式
JDK 序列化方式 (默認)
org.springframework.data.redis.serializer.JdkSerializationRedisSerializer ,默認情況下,RedisTemplate 使用該數據列化方式。
我們來看下源碼 RedisTemplate#afterPropertiesSet()
Spring Boot 自動化配置 RedisTemplate Bean 對象時,就未設置默認的序列化方式。
絕大多數情況下,不推薦使用 JdkSerializationRedisSerializer 進行序列化。主要是不方便人工排查數據。
我們來做個測試
運行單元測試
看不懂呀 ,老哥
KEY 前面帶著奇怪的 16 進制字符 , VALUE 也是一串奇怪的 16 進制字符 。。。。。
為什么是這樣一串奇怪的 16 進制? ObjectOutputStream#writeString(String str, boolean unshared) 實際就是標志位 + 字符串長度 + 字符串內容
KEY 被序列化成這樣,線上通過 KEY 去查詢對應的 VALUE非常不方便,所以 KEY 肯定是不能被這樣序列化的。
VALUE 被序列化成這樣,除了閱讀可能困難一點,不支持跨語言外,實際上也沒還OK。不過,實際線上場景,還是使用 JSON 序列化居多。
String 序列化方式
org.springframework.data.redis.serializer.StringRedisSerializer ,字符串和二進制數組的直接轉換
絕大多數情況下,我們 KEY 和 VALUE 都會使用這種序列化方案。
JSON 序列化方式
org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer 使用 Jackson 實現 JSON 的序列化方式,并且從 Generic 單詞可以看出,是支持所有類。
public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName) {.......... if (StringUtils.hasText(classPropertyTypeName)) {mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName);} else {mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);}}classPropertyTypeName 不為空的話,使用傳入對象的 classPropertyTypeName 屬性對應的值,作為默認類型(Default Typing) ,否則使用傳入對象的類全名,作為默認類型(Default Typing)。
我們來思考下,在將一個對象序列化成一個字符串,怎么保證字符串反序列化成對象的類型呢?Jackson 通過 Default Typing ,會在字符串多冗余一個類型,這樣反序列化就知道具體的類型了
先說個結論
標準JSON
{"id": 100,"name": "小工匠","sex": "Male" }使用 Jackson Default Typing 機制序列化
{"@class": "com.artisan.domain.Artisan","id": 100,"name": "小工匠","sex": "Male" }示例
測試一把
【配置類】
@Beanpublic RedisTemplate<String, Object> redisTemplate() {// 創建 RedisTemplate 對象RedisTemplate<String, Object> template = new RedisTemplate<>();// 設置 RedisConnection 工廠。 它就是實現多種 Java Redis 客戶端接入的秘密工廠template.setConnectionFactory(connectionFactory);// 使用 String 序列化方式,序列化 KEY 。template.setKeySerializer(RedisSerializer.string());// 使用 JSON 序列化方式(庫是 Jackson ),序列化 VALUE 。template.setValueSerializer(RedisSerializer.json());return template;}【單元測試】
@Beanpublic RedisTemplate<String, Object> redisTemplate() {// 創建 RedisTemplate 對象RedisTemplate<String, Object> template = new RedisTemplate<>();// 設置 RedisConnection 工廠。 它就是實現多種 Java Redis 客戶端接入的秘密工廠template.setConnectionFactory(connectionFactory);// 使用 String 序列化方式,序列化 KEY 。template.setKeySerializer(RedisSerializer.string());// 使用 JSON 序列化方式(庫是 Jackson ),序列化 VALUE 。template.setValueSerializer(RedisSerializer.json());return template;}【結果】
是不是多了@class 屬性,反序列化的對象的類型就可以從這里獲取到。
@class 屬性看似完美解決了反序列化后的對象類型,但是帶來 JSON 字符串占用變大,所以實際項目中,我們很少采用 Jackson2JsonRedisSerializer
XML 序列化方式
org.springframework.data.redis.serializer.OxmSerializer使用 Spring OXM 實現將對象和 String 的轉換,從而 String 和二進制數組的轉換。 沒見過哪個項目用過,不啰嗦了
spring boot整合redis實現RedisTemplate三分鐘快速入門
引入依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>RedisTemplate五種數據結構的操作
- redisTemplate.opsForValue(); //操作字符串
- redisTemplate.opsForHash(); //操作hash
- redisTemplate.opsForList(); //操作list
- redisTemplate.opsForSet(); //操作set
- redisTemplate.opsForZSet(); //操作有序zset
RedisTemplate方法講解
判斷key是否存在
/*** 判斷key是否存在*/@GetMapping("haskey")public boolean hasKey(String key) {return redisTemplate.hasKey(key);}獲取指定的key的失效時間
/*** 指定key的失效時間*/@GetMapping("expire")public void expire(String key, long time) {//參數一:key//參數二:睡眠時間//參數三:睡眠時間單位 TimeUnit.DAYS 天 TimeUnit.HOURS 小時 。。。redisTemplate.expire(key, time, TimeUnit.MINUTES);}根據key獲取過期時間
/*** 根據key獲取過期時間*/@GetMapping("getexpire")public long getExpire(String key) {Long expire = redisTemplate.getExpire(key);return expire;}根據key刪除reids中緩存數據
/*** 根據key刪除reids中緩存數據*/@GetMapping("delredisbykey")public void delete(String key) {redisTemplate.delete(key);}保存和讀取String
/*** 保存和讀取String*/@GetMapping("stringredisdemo")public String stringredisdemo() {//設置過期時間為1分鐘redisTemplate.opsForValue().set("key1", "value1", 1, TimeUnit.MINUTES);redisTemplate.opsForValue().set("key2", "value2");redisTemplate.opsForValue().set("key3", "value3");//讀取redis數據String result1 = redisTemplate.opsForValue().get("key1").toString();String result2 = redisTemplate.opsForValue().get("key2").toString();String result3 = redisTemplate.opsForValue().get("key3").toString();System.out.println("緩存結果為:result:" + result1 + " " + result2 + " " + result3);return "緩存結果為:result:" + result1 + " " + result2 + " " + result3;}保存和讀取list
/*** 保存和讀取list*/@GetMapping("listredisdemo")public String listredisdemo() {List<String> list1 = new ArrayList<>();list1.add("a1");list1.add("a2");list1.add("a3");List<String> list2 = new ArrayList<>();list2.add("b1");list2.add("b2");list2.add("b3");redisTemplate.opsForList().leftPush("listkey1", list1);redisTemplate.opsForList().rightPush("listkey2", list2);List<String> resultList1 = (List<String>) redisTemplate.opsForList().leftPop("listkey1");List<String> resultList2 = (List<String>) redisTemplate.opsForList().rightPop("listkey2");System.out.println("resultList1:" + resultList1);System.out.println("resultList2:" + resultList2);return "成功";}Hash結構,保存和讀取map
/*** Hash結構,保存和讀取map*/@GetMapping("mapredisdemo")public String mapredisdemo() {Map<String, String> map = new HashMap<>();map.put("key1", "value1");map.put("key2", "value2");map.put("key3", "value3");map.put("key4", "value4");map.put("key5", "value5");redisTemplate.opsForHash().putAll("map1", map);Map<String, String> resultMap = redisTemplate.opsForHash().entries("map1");List<String> reslutMapList = redisTemplate.opsForHash().values("map1");Set<String> resultMapSet = redisTemplate.opsForHash().keys("map1");String value = (String) redisTemplate.opsForHash().get("map1", "key1");System.out.println("value:" + value);System.out.println("resultMapSet:" + resultMapSet);System.out.println("resultMap:" + resultMap);System.out.println("resulreslutMapListtMap:" + reslutMapList);return "成功";}保存和讀取Set
/*** 保存和讀取Set*/@GetMapping("setredisdemo")public String getredisdemo() {SetOperations<String, String> set = redisTemplate.opsForSet();set.add("key1", "value1");set.add("key1", "value2");set.add("key1", "value3");Set<String> resultSet = redisTemplate.opsForSet().members("key1");System.out.println("resultSet:" + resultSet);return "resultSet:" + resultSet;}保存和讀取zset
/*** 保存和讀取zset*/@GetMapping("zsetredisdemo")public String zsetredisdemo() {ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5", 9.6);ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("zset-6", 9.9);Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();tuples.add(objectTypedTuple1);tuples.add(objectTypedTuple2);System.out.println(redisTemplate.opsForZSet().add("zset1", tuples));System.out.println(redisTemplate.opsForZSet().range("zset1", 0, -1));return "成功";}完整示例代碼
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.DefaultTypedTuple; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import java.util.*; import java.util.concurrent.TimeUnit;@RestController public class ReidsDemo {@AutowiredRedisTemplate redisTemplate;/*** 指定key的失效時間*/@GetMapping("expire")public void expire(String key, long time) {//參數一:key//參數二:睡眠時間//參數三:睡眠時間單位 TimeUnit.DAYS 天 TimeUnit.HOURS 小時 。。。redisTemplate.expire(key, time, TimeUnit.MINUTES);}/*** 根據key獲取過期時間*/@GetMapping("getexpire")public long getExpire(String key) {Long expire = redisTemplate.getExpire(key);return expire;}/*** 判斷key是否存在*/@GetMapping("haskey")public boolean hasKey(String key) {return redisTemplate.hasKey(key);}/*** 根據key刪除reids中緩存數據*/@GetMapping("delredisbykey")public void delete(String key) {redisTemplate.delete(key);}/*** 保存和讀取String*/@GetMapping("stringredisdemo")public String stringredisdemo() {//設置過期時間為1分鐘redisTemplate.opsForValue().set("key1", "value1", 1, TimeUnit.MINUTES);redisTemplate.opsForValue().set("key2", "value2");redisTemplate.opsForValue().set("key3", "value3");//讀取redis數據String result1 = redisTemplate.opsForValue().get("key1").toString();String result2 = redisTemplate.opsForValue().get("key2").toString();String result3 = redisTemplate.opsForValue().get("key3").toString();System.out.println("緩存結果為:result:" + result1 + " " + result2 + " " + result3);return "緩存結果為:result:" + result1 + " " + result2 + " " + result3;}/*** 保存和讀取list*/@GetMapping("listredisdemo")public String listredisdemo() {List<String> list1 = new ArrayList<>();list1.add("a1");list1.add("a2");list1.add("a3");List<String> list2 = new ArrayList<>();list2.add("b1");list2.add("b2");list2.add("b3");redisTemplate.opsForList().leftPush("listkey1", list1);redisTemplate.opsForList().rightPush("listkey2", list2);List<String> resultList1 = (List<String>) redisTemplate.opsForList().leftPop("listkey1");List<String> resultList2 = (List<String>) redisTemplate.opsForList().rightPop("listkey2");System.out.println("resultList1:" + resultList1);System.out.println("resultList2:" + resultList2);return "成功";}/*** Hash結構,保存和讀取map*/@GetMapping("mapredisdemo")public String mapredisdemo() {Map<String, String> map = new HashMap<>();map.put("key1", "value1");map.put("key2", "value2");map.put("key3", "value3");redisTemplate.opsForHash().putAll("map1", map);Map<String, String> resultMap = redisTemplate.opsForHash().entries("map1");List<String> reslutMapList = redisTemplate.opsForHash().values("map1");Set<String> resultMapSet = redisTemplate.opsForHash().keys("map1");String value = (String) redisTemplate.opsForHash().get("map1", "key1");System.out.println("value:" + value);System.out.println("resultMapSet:" + resultMapSet);System.out.println("resultMap:" + resultMap);System.out.println("resulreslutMapListtMap:" + reslutMapList);return "成功";}/*** 保存和讀取Set*/@GetMapping("setredisdemo")public String getredisdemo() {SetOperations<String, String> set = redisTemplate.opsForSet();set.add("key1", "value1");set.add("key1", "value2");set.add("key1", "value3");Set<String> resultSet = redisTemplate.opsForSet().members("key1");System.out.println("resultSet:" + resultSet);return "resultSet:" + resultSet;}/*** 保存和讀取zset*/@GetMapping("zsetredisdemo")public String zsetredisdemo() {ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5", 9.6);ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("zset-6", 9.9);Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();tuples.add(objectTypedTuple1);tuples.add(objectTypedTuple2);System.out.println(redisTemplate.opsForZSet().add("zset1", tuples));System.out.println(redisTemplate.opsForZSet().range("zset1", 0, -1));return "成功";} }序列化
直接粘貼在項目中即可
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;/* *序列化*/ @Configuration public class MyRedisConfig {@Bean(name = "redisTemplate")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();//參照StringRedisTemplate內部實現指定序列化器redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(keySerializer());redisTemplate.setHashKeySerializer(keySerializer());redisTemplate.setValueSerializer(valueSerializer());redisTemplate.setHashValueSerializer(valueSerializer());return redisTemplate;}private RedisSerializer<String> keySerializer(){return new StringRedisSerializer();}//使用Jackson序列化器private RedisSerializer<Object> valueSerializer(){return new GenericJackson2JsonRedisSerializer();} }總結
以上是生活随笔為你收集整理的深入理解 Redis Template及4种序列化方式__spring boot整合redis实现RedisTemplate三分钟快速入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DHL全球货运与第四范式达成合作
- 下一篇: WAIC2020开幕在即,第四范式亮点抢