关于java:笑小枫的SpringBoot系列八SpringBoot集成Redis

41次阅读

共计 12480 个字符,预计需要花费 32 分钟才能阅读完成。

SpringBoot 集成 Redis

Redis 原生命令大全,作者整顿的很具体,大部分命令转化为 java 命令根本也是关键词

Redis 命令参考

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

引入依赖

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

  • pom.xml

    <!-- 引入 redis 依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- 引入 redis 连接池的依赖 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
    </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 汇合的数据
  • 模仿操作 ZSet 有序汇合的数据

监听 redis Key 过期的事件

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

如果数据要求比拟谨严,请慎用此性能

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

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

K:keyspace 事件,事件以__keyspace@<db>__为前缀进行公布;E:keyevent 事件,事件以__keyevent@<db>__为前缀进行公布;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
CSDN:https://zhangfz.blog.csdn.net
本文源码:https://github.com/hack-feng/maple-demo

正文完
 0