zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

深入理解 Redis Template及4种序列化方式__spring boot整合redis实现RedisTemplate三分钟快速入门

RedisSpringBoot入门 实现 快速 方式 深入
2023-09-14 09:06:17 时间

概述

使用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"
}

示例

测试一把

【配置类】

 @Bean
 public 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;
 }

【单元测试】

 @Bean
 public 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 {

  @Autowired
  RedisTemplate 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();
  } 
}