【笑小枫的SpringBoot系列】【八】SpringBoot集成Redis

SpringBoot集成Redis

Redis原生命令大全,作者整理的很详细,大部分命令转化为java命令基本也是关键词

Redis 命令参考

接下来开始我们的正题,一起学习下,SpringBoot整合Redis

引入依赖

pom文件不贴全部代码了,依赖有些多了,占据的篇幅过大,只贴新增的吧

  • pom.xml

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>


<dependency>
    <groupId>org.apache.commonsgroupId>
    <artifactId>commons-pool2artifactId>
dependency>

Redis配置

随着配置越来越多,这里就不贴全部了,注意和datasource同级,在spring的下级,要去掉spring哈

  • 添加application.yml 配置
spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    timeout: 5000
    lettuce:
      pool:
        max-active: 32
        max-wait: -1
        max-idle: 16
        min-idle: 8
  • 在config包下添加RedisConfig.java 配置类
package com.maple.demo.config;

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author 笑小枫
 * @date 2022/07/19
 **/
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        //使用fastjson序列化
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // value值的序列化采用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        // key的序列化采用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

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

工具类

配置完配置,其实我们的Redis就已经集成了,SpringBoot的starter是真的香,后面我们会讲解一下如何制作我们自己的starter。

下面配置一下redis常用的工具类,在util包下创建RedisUtil.java

package com.maple.demo.util;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis常用的一些操作
 *
 * @author 笑小枫
 * @date 2022/07/19
 */
@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 写入缓存
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<String, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存设置时效时间
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<String, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 更新缓存
     */
    public boolean getAndSet(final String key, String value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().getAndSet(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 批量删除对应的value
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     */
    public void removePattern(final String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (CollectionUtils.isNotEmpty(keys)) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     */
    public boolean exists(final String key) {
        Boolean isExists = redisTemplate.hasKey(key);
        return BooleanUtils.isTrue(isExists);
    }

    /**
     * 读取缓存
     */
    public Object get(final String key) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        return operations.get(key);
    }

    /**
     * 哈希 添加
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key, hashKey, value);
    }

    /**
     * 哈希获取数据
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 列表添加
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k, v);
    }

    /**
     * 列表获取
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }

    /**
     * 集合添加
     */
    public void addSet(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key, value);
    }

    /**
     * 删除集合下的所有值
     */
    public void removeSetAll(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        Set<Object> objectSet = set.members(key);
        if (objectSet != null && !objectSet.isEmpty()) {
            for (Object o : objectSet) {
                set.remove(key, o);
            }
        }
    }

    /**
     * 判断set集合里面是否包含某个元素
     */
    public Boolean isMember(String key, Object member) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.isMember(key, member);
    }

    /**
     * 集合获取
     */
    public Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     */
    public void zAdd(String key, Object value, double source) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key, value, source);
    }

    /**
     * 有序集合获取指定范围的数据
     */
    public Set<Object> rangeByScore(String key, double source, double source1) {
        ZSetOperations<String, Object> zSet = redisTemplate.opsForZSet();
        return zSet.rangeByScore(key, source, source1);
    }

    /**
     * 有序集合升序获取
     */
    public Set<Object> range(String key, Long source, Long source1) {
        ZSetOperations<String, Object> zSet = redisTemplate.opsForZSet();
        return zSet.range(key, source, source1);
    }

    /**
     * 有序集合降序获取
     */
    public Set<Object> reverseRange(String key, Long source, Long source1) {
        ZSetOperations<String, Object> zSet = redisTemplate.opsForZSet();
        return zSet.reverseRange(key, source, source1);
    }
}

测试一下吧

编写我们的测试类

package com.maple.demo.controller;

import com.alibaba.fastjson.JSON;
import com.maple.demo.util.RedisUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

/**
 * @author 笑小枫
 * @date 2022/7/20
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/example")
@Api(tags = "实例演示-Redis接口文档")
public class TestRedisController {

    private final RedisUtil redisUtil;

    @PutMapping("/insertStr")
    @ApiOperation(value = "插入String类型的数据到redis")
    public void insertStr(String key, String value) {
        redisUtil.set(key, value);
    }

    @PostMapping("/getStr")
    @ApiOperation(value = "根据key获取redis的数据")
    public String getStr(String key) {
        return String.valueOf(redisUtil.get(key));
    }

    @DeleteMapping("/deleteStr")
    @ApiOperation(value = "根据key删除redis的数据")
    public Boolean deleteStr(String key) {
        redisUtil.remove(key);
        return redisUtil.exists(key);
    }

    @PostMapping("/operateMap")
    @ApiOperation(value = "模拟操作Map集合的数据")
    public Object operateMap() {
        redisUtil.hmSet("maple:map", "xiaofeng", "笑小枫");
        return redisUtil.hmGet("maple:map", "xiaofeng");
    }

    @PostMapping("/operateList")
    @ApiOperation(value = "模拟操作List集合的数据")
    public String operateList() {
        String listKey = "maple:list";
        redisUtil.lPush(listKey, "小枫");
        redisUtil.lPush(listKey, "小明");
        redisUtil.lPush(listKey, "小枫");
        return JSON.toJSONString(redisUtil.lRange(listKey, 0, 2));
    }

    @PostMapping("/operateSet")
    @ApiOperation(value = "模拟操作Set集合的数据")
    public String operateSet() {
        String listKey = "maple:set";
        redisUtil.addSet(listKey, "小枫");
        redisUtil.addSet(listKey, "小明");
        redisUtil.addSet(listKey, "小枫");
        log.info("集合中是否包含小枫" + redisUtil.isMember(listKey, "小枫"));
        log.info("集合中是否包含小红" + redisUtil.isMember(listKey, "小红"));
        return JSON.toJSONString(redisUtil.setMembers(listKey));
    }

    @PostMapping("/operateZSet")
    @ApiOperation(value = "模拟操作ZSet有序集合的数据")
    public String operateZSet() {
        String listKey = "maple:zSet";
        redisUtil.zAdd(listKey, "小枫", 8);
        redisUtil.zAdd(listKey, "小明", 1);
        redisUtil.zAdd(listKey, "小红", 12);
        redisUtil.zAdd(listKey, "大明", 5);
        redisUtil.zAdd(listKey, "唐三", 10);
        redisUtil.zAdd(listKey, "小舞", 9);
        // 降序获取source最高的5条数据
        return JSON.toJSONString(redisUtil.reverseRange(listKey, 0L, 4L));
    }
}

具体的返回结果我就不一一贴图了,自己建站,流量和网速永远都是一大诟病(哭穷)

简单贴两张吧,怕你们说我敷衍

  • 模拟操作List集合的数据

【笑小枫的SpringBoot系列】【八】SpringBoot集成Redis_第1张图片

  • 模拟操作ZSet有序集合的数据

【笑小枫的SpringBoot系列】【八】SpringBoot集成Redis_第2张图片

监听redis Key过期的事件

  • 开始redis过期Key的监听事件

如果数据要求比较严谨,请慎用此功能

修改redis.conf配置为文件,我用的Redis3.2版本(比较古老了),windows下是redis.windows-service.conf文件

【笑小枫的SpringBoot系列】【八】SpringBoot集成Redis_第3张图片

看一下notify-keyspace-events Ex是否被注释(默认是注释),放开注释即可。

K:keyspace事件,事件以__keyspace@__为前缀进行发布;        
E:keyevent事件,事件以__keyevent@__为前缀进行发布;        
g:一般性的,非特定类型的命令,比如del,expire,rename等;       
$:字符串特定命令;        
l:列表特定命令;        
s:集合特定命令;        
h:哈希特定命令;        
z:有序集合特定命令;        
x:过期事件,当某个键过期并删除时会产生该事件;        
e:驱逐事件,当某个键因maxmemore策略而被删除时,产生该事件;        
A:g$lshzxe的别名,因此”AKE”意味着所有事件。
  • 修改我们的RedisConfig.java文件,添加开启监听redis Key过期事件,完整配置如下
package com.maple.demo.config;

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author 笑小枫
 * @date 2022/07/19
 **/
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        //使用fastjson序列化
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // value值的序列化采用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        // key的序列化采用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

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

    /**
     * 开启监听redis Key过期事件
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory){
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}
  • 定义监听器RedisKeyExpireListener
package com.maple.demo.listener;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

/**
 * Redis监听key过期
 *
 * @author 笑小枫
 * @date 2022/07/19
 **/
@Slf4j
@Component
public class RedisKeyExpireListener extends KeyExpirationEventMessageListener {

    public RedisKeyExpireListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expireKey = message.toString();
        // 根据过期的key处理对应的业务逻辑
        log.info(expireKey + "已过期-------------------");
    }
}

小结

好啦,本文就到这里了,我们简单的总结一下,主要介绍了以下内容

  • 本文核心:SpringBoot继承redis
  • SpringBoot常用的redis操作演示
  • 监听Redis的key过期机制

关于笑小枫

本章到这里结束了,喜欢的朋友关注一下我呦,大伙的支持,就是我坚持写下去的动力。
老规矩,懂了就点赞收藏;不懂就问,日常在线,我会就会回复哈~
笑小枫个人博客:https://www.xiaoxiaofeng.com
本文源码:https://github.com/hack-feng/maple-demo

你可能感兴趣的:(1024程序员节)