【Redis】Redis 的学习教程(五)之 SpringBoot 集成 Redis

在前几篇文章中,我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。

在当前流行的微服务以及分布式集群环境下,Redis 的使用场景可以说非常的广泛,能解决集群环境下系统中遇到的不少技术问题,在此列举几个使用 Redis 经常用到的功能:

  • 分布式缓存:在分布式的集群架构中,将缓存存储在内存中会出现很多的问题,比如用户回话信息,因为这部分信息需要与其他机器共享,此时利用 Redis 可以很好的解决机器之间数据共享的问题,缓存也是 Redis 中使用最多的场景
  • 分布式锁:在高并发的情况下,我们需要一个锁来防止并发带来的脏数据,Java 自带的锁机制显然对进程间的并发并不好使,此时利用 Redis 的单线程特性,实现分布式锁控制
  • 接口限流:在集群环境下,可以利用 Redis 的分布式自增 ID 功能,精准的统计每个接口在指定时间内的请求次数,利用这个特性,可以定向限制某个接口恶意频刷

当然 Redis 的使用场景并不仅仅只有这么多,还有很多未列出的场景,如发布/订阅,分布锁集合等。

现实中我们大部分的微服务项目,都是基于 SpringBoot 框架进行快速开发,在 SpringBoot 项目中我们应该如何使用 Redis 呢?代码实践如下。

1. 开发环境

  • IDEA:2021.3.3
  • JDK:1.8
  • SpringBoot:2.7.14
  • Maven:3.6.3

咱们通过程序是不能直接连接 Redis,得利用客户端工具才能进行连接。比较常用的有两种:Jedis、Lettuce。

在 springboot 1.5.x 版本的默认的 Redis 客户端是 Jedis 实现的,springboot 2.x 版本中默认客户端是用 lettuce实现的。

既然 LettuceJedis 的都是连接 Redis 的客户端,那么它们有什么区别呢?

  • Jedis 在实现上是直连 Redis Server,多线程环境下非线程安全,除非使用连接池,为每个 Redis 实例增加 物理连接
  • Lettuce 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个 RedisConnection,它利用 Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序

2. 代码实战

在 SpringBoot 集成的 Redis 时,我这里采用的是 Lettuce

2.1 默认使用 Lettuce

1、引入依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、添加配置:

yml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password:
    timeout: 2000s
    # 配置文件中添加 lettuce.pool 相关配置,则会使用到lettuce连接池
    lettuce:
      pool:
        max-active: 8  # 连接池最大连接数(使用负值表示没有限制) 默认为8
        max-wait: -1ms # 接池最大阻塞等待时间(使用负值表示没有限制) 默认为-1ms
        max-idle: 8    # 连接池中的最大空闲连接 默认为8
        min-idle: 0    # 连接池中的最小空闲连接 默认为 0

2.2 换成 Jedis

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

jedis 中会引入 commons-pool2 依赖,如果没有引入:

xml 复制代码
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

添加配置:

yml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password:
    timeout: 2000s
    # 配置文件中添加 jedis.pool 相关配置,则会使用到 jedis 连接池
    jedis:
      pool:
        max-active: 10
        max-idle: 8
        min-idle: 0
        max-wait: 60s

2.3 使用 RedisTemplate 对象操作 Redis

在 SpringBoot 中,是使用 RedisTemplate 对象来操作 Redis 的。

在 Springboot 自动配置原理中,涉及到以下两方面:

  1. SpringBoot 中所有的配置类,都有一个自动配置类。RedisAutoConfiguration
  2. 自动配置类都会绑定一个配置文件 properties。RedisProperties

RedisAutoConfiguration.class

java 复制代码
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"})
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

StringRedisTemplate

java 复制代码
public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

通过上述看,注入了两个类型的 RedisTemplate 对象:

  1. 如果没有注入名称为 redisTemplate 的 RedisTemplate 对象,则注入 RedisTemplate<Object, Object> 对象
  2. 注入 StringRedisTemplate 对象。而 StringRedisTemplate 对象又是继承 RedisTemplate<String, String> 类的

使用上面两个类型 RedisTemplate 的对象操作 Redis:

java 复制代码
@RestController
@RequestMapping("/redis")
public class RedisController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @GetMapping("/set")
    public String set() {
        stringRedisTemplate.opsForValue().set("name", "zzc");
        redisTemplate.opsForValue().set("age", "zzc");
        return "set";
    }
}

调用成功后,我们使用 Redis 客户端工具进行查看:

发现:

redisTemplate.opsForValue().set("age", "zzc"); 操作的 keyvalue 都变成乱码。

springboot系列------redisTemplate和stringRedisTemplate对比、redisTemplate几种序列化方式比较

通过 debug 源代码知:RedisTemplate<Object, Object> 的 key、value 序列化默认都是 JdkSerializationRedisSerializer,序列化方法如下:

java 复制代码
default byte[] serializeToByteArray(T object) throws IOException {
   ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
   this.serialize(object, out);
   return out.toByteArray();
}

public void serialize(Object object, OutputStream outputStream) throws IOException {
    if (!(object instanceof Serializable)) {
        throw new IllegalArgumentException(this.getClass().getSimpleName() + " requires a Serializable payload but received an object of type [" + object.getClass().getName() + "]");
    } else {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
    }
}

将 key、value 进行序列化成 byte 类型,所以,看上去会乱码。(可读性差)

StringRedisTemplate 对象使用 RedisSerializer 序列的

2.4 自定义 RedisTemplate 对象

为了可读性,可以使用 StringRedisTemplate 类,但有一个要求:key、value 都要求是 String 类型。

但这就有一个问题,我们平时用得对象比较多,那又如何存储对象呢?

例如,我们这里的 User 对象:

java 复制代码
public class User {
    private String id;
    private String userName;
    private Integer age;
    // getter/setter
}

由于 RedisTemplate<String, String> 的泛型参数都是 String 类型的,那我们只需要将 Java 对象转换为 String 对象即可:

java 复制代码
@Override
public boolean addUser(User user) {
   redisTemplate.opsForValue().set("user", JSON.toJSONString(user));
   String strUser = redisTemplate.opsForValue().get("user1");
   User resultUser = JSON.parseObject(strUser, User.class);
   return true;
}

存 Redis 之前,将 Java 对象转换为 Json 字符串;读取后,将 Json 字符串转换为 Java 对象。

这样做确实可行,但是,如果要存储的对象较多的话,那岂不是要重复地将 Java 对象转换为 Json 字符串?这样是不是很繁琐?

继续看 RedisAutoConfiguration.class 源码,发现被注入的 RedisTemplate<Object, Object>@ConditionalOnMissingBean(name="redisTemplate") 注解修饰:如果 Spring 容器中有了 RedisTemplate 对象了,这个自动配置的 RedisTemplate 不会实例化。因此我们可以直接自己写个配置类,配置 RedisTemplate。并且,我们更希望 key 是 String 类型,value 是 Object 类型(String、int、对象等类型):

java 复制代码
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        // json 序列化配置
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 所有的 key 采用 string 的序列化
        template.setKeySerializer(stringRedisSerializer);
        // 所有的 value 采用 jackson 的序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash 的 key 采用 string 的序列化
        template.setHashKeySerializer(stringRedisSerializer);
        // hash 的 value 采用 jackson 的序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

测试:

java 复制代码
@RestController
@RequestMapping("/redis")
public class RedisController {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/set")
    public String set() {
        User user = new User();
        user.setName("zzc");
        user.setAge(18);
        redisTemplate.opsForValue().set("user", user);
        return "set";
    }

}

2.5 RedisUtil 工具类

java 复制代码
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    // ============================Common=============================
    
    public void setHashValueSerializer(RedisSerializer serializer) {
        redisTemplate.setHashValueSerializer(serializer);
    }

    /**
     * 指定缓存失效时间
     *
     * @author zzc
     * @date 2023/8/2 11:06
     * @param key    键
     * @param time   时间(秒)
     * @return boolean
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @author zzc
     * @date 2023/8/2 11:07
     * @param key    键 不能为null
     * @return long  时间(秒) 返回0代表为永久有效
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @author zzc
     * @date 2023/8/2 11:07
     * @param key      键
     * @return boolean 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @author zzc
     * @date 2023/8/2 11:08
     * @param key   可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                //springboot2.4后用法
                redisTemplate.delete(Arrays.asList(key));
            }
        }
    }

    /**
     * 获取指定前缀的一系列key
     * 使用scan命令代替keys, Redis是单线程处理,keys命令在KEY数量较多时,
     * 操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求
     *
     * @author zzc
     * @date 2023/8/2 11:53
     * @param keyPrefix
     * @return java.util.Set<java.lang.String>
     */
    public Set<String> keys(String keyPrefix) {
        String realKey = keyPrefix + "*";
        try {
            return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
                Set<String> binaryKeys = new HashSet<>();
                //springboot2.4后用法
                Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(realKey).count(Integer.MAX_VALUE).build());
                while (cursor.hasNext()) {
                    binaryKeys.add(new String(cursor.next()));
                }

                return binaryKeys;
            });
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 删除指定前缀的一系列key
     *
     * @author zzc
     * @date 2023/8/2 11:53
     * @param keyPrefix
     */
    public void removeAll(String keyPrefix) {
        try {
            Set<String> keys = keys(keyPrefix);
            redisTemplate.delete(keys);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    
    // 执行 lua 脚本
    public <T> T execute(RedisScript<T> script, List<String> keys, Object... args) {
        return redisTemplate.execute(script, keys, args);
    }

    public boolean convertAndSend(String channel, Object message) {
        if (!StringUtils.hasText(channel)) {
            return false;
        }
        try {
            redisTemplate.convertAndSend(channel, message);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    // ============================String=============================

    /**
     * 普通缓存获取
     *
     * @author zzc
     * @date 2023/8/2 11:08
     * @param key                   键
     * @return java.lang.Object     值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @author zzc
     * @date 2023/8/2 11:09
     * @param key           键
     * @param value         值
     * @return boolean      true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置过期时间
     *
     * @author zzc
     * @date 2023/8/2 11:09
     * @param key     键
     * @param value   值
     * @param time    时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return boolean  true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     * @author zzc
     * @date 2023/8/2 11:10
     * @param key         键
     * @param delta       要增加几(大于0)
     * @return java.lang.Long
     */
    public Long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @author zzc
     * @date 2023/8/2 11:11
     * @param key                 键
     * @param delta               要减少几(小于0)
     * @return java.lang.Long
     */
    public Long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    public boolean setNx(String key, Object value) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));
    }
    
    public boolean setNx(String key, Object value, long time) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS));
    }
    
    public void multiSet(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }

    public List<Object> multiGet(List<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }


    // ================================Hash=================================

    /**
     * Hash Get
     * @author zzc
     * @date 2023/8/2 11:12
     * @param key                键 不能为null
     * @param item               项 不能为null
     * @return java.lang.Object
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取Key对应的所有键值
     *
     * @author zzc
     * @date 2023/8/2 11:12
     * @param key                                                  键
     * @return java.util.Map<java.lang.Object,java.lang.Object>    对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * Hash Set
     *
     * @author zzc
     * @date 2023/8/2 11:13
     * @param key         键
     * @param map         对应多个键值
     * @return boolean    true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Hash Set 并设置过期时间
     * @author zzc
     * @date 2023/8/2 11:13
     * @param key        键
     * @param map        对应多个键值
     * @param time       时间(秒)
     * @return boolean   true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有过期时间,这里将会替换原有的过期时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的项
     *
     * @author zzc
     * @date 2023/8/2 11:38
     * @param key   键 不能为null
     * @param item  项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断 hash 表中是否有该项的值
     *
     * @author zzc
     * @date 2023/8/2 11:38
     * @param key    键 不能为null
     * @param item   项 不能为null
     * @return boolean  true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash 递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @author zzc
     * @date 2023/8/2 11:40
     * @param key    键
     * @param item   项
     * @param by     要增加几(大于0)
     * @return double
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    public Long hincr(String key, String item, long by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash 递减
     *
     * @author zzc
     * @date 2023/8/2 11:40
     * @param key    键
     * @param item   项
     * @param by     要减少几(小于0)
     * @return double
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    public List<Object> hmultiGet(String key, List<Object> items) {
        return redisTemplate.opsForHash().multiGet(key, items);
    }


    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @author zzc
     * @date 2023/8/2 11:41
     * @param key
     * @return java.util.Set<java.lang.Object>
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @author zzc
     * @date 2023/8/2 11:41
     * @param key       键
     * @param value     值
     * @return boolean  true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @author zzc
     * @date 2023/8/2 11:42
     * @param key       键
     * @param values    值 可以是多个
     * @return long     成功个数
     */
    public Long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 将 set 数据放入缓存
     *
     * @author zzc
     * @date 2023/8/2 11:42
     * @param key     键
     * @param time    时间(秒)
     * @param values  值 可以是多个
     * @return long   成功个数
     */
    public Long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @author zzc
     * @date 2023/8/2 11:45
     * @param key
     * @return long
     */
    public Long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 移除值为value的
     *
     * @author zzc
     * @date 2023/8/2 11:45
     * @param key    键
     * @param values 值 可以是多个
     * @return long  移除的个数
     */
    public Long setRemove(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }


    // ===============================List=================================

    /**
     * 获取list缓存的内容
     *
     * @author zzc
     * @date 2023/8/2 11:46
     * @param key      键
     * @param start    开始
     * @param end      结束 0 到 -1代表所有值
     * @return java.util.List<java.lang.Object>
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @author zzc
     * @date 2023/8/2 11:47
     * @param key
     * @return long
     */
    public Long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @author zzc
     * @date 2023/8/2 11:47
     * @param key     键
     * @param index   索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return java.lang.Object
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @author zzc
     * @date 2023/8/2 11:48
     * @param key       键
     * @param value     值
     * @return boolean
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @author zzc
     * @date 2023/8/2 11:48
     * @param key       键
     * @param value     值
     * @param time  时间(秒)
     * @return boolean
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @author zzc
     * @date 2023/8/2 11:49
     * @param key        键
     * @param value      值
     * @return boolean   时间(秒)
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @author zzc
     * @date 2023/8/2 11:49
     * @param key        键
     * @param value      值
     * @param time       时间(秒)
     * @return boolean
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @author zzc
     * @date 2023/8/2 11:51
     * @param key     键
     * @param index   索引
     * @param value   值
     * @return boolean
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @author zzc
     * @date 2023/8/2 11:51
     * @param key    键
     * @param count  移除多少个
     * @param value  值
     * @return long  移除的个数
     */
    public Long lRemove(String key, long count, Object value) {
        try {
            return redisTemplate.opsForList().remove(key, count, value);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

}
相关推荐
zwjapple23 分钟前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
DKPT1 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
DuelCode3 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
优创学社23 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
好好研究3 小时前
学习栈和队列的插入和删除操作
数据结构·学习
幽络源小助理3 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
猴哥源码4 小时前
基于Java+springboot 的车险理赔信息管理系统
java·spring boot
Hello.Reader4 小时前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
新中地GIS开发老师4 小时前
新发布:26考研院校和专业大纲
学习·考研·arcgis·大学生·遥感·gis开发·地理信息科学
SH11HF5 小时前
小菜狗的云计算之旅,学习了解rsync+sersync实现数据实时同步(详细操作步骤)
学习·云计算