SpringBoot中如何正确使用Redis(详细介绍,原理讲解,企业版)

news2024/12/28 19:48:12

1.引入Redis依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.配置Redis的连接信息(application.yml)

实际开发中有两个一个是开发环境application-dev,一个是生产环境application-prod。

spring:
  redis:
    database: 0           # Redis服务器数据库
    host: 127.0.0.1       # Redis服务器地址
    port: 6379            # Redis服务器连接端口
    password: 123456      # Redis服务器连接密码(默认为空)
    timeout: 6000         # Redis连接超时时间(毫秒)

3.创建RedisConfig主要是通过@Bean注入RedisTemplate。

一般我们配置RedisTemplate的时候,配置类一大堆东西,接下来我从最简单的配置到最终的配置依次测试和演示为什么需要这么配置的原因。

(1)最简单的配置,直接new RedisTemplate通过@Bean注入,写个简单的controller测试。

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate redisTemplate(){
        return new RedisTemplate();
    }
}
@RestController
@RequestMapping("/test")
public class RedisController {

    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("/aaa")
    public String testA(){
        redisTemplate.opsForValue().set("test","123456");
        return "success";
    }
}

接下来我么直接启动项目来看。

很明显直接报错,说我们需要一个Redis的连接工厂,所以接下来我们去配置Redis的连接工厂。

(2) 配置Reids的连接工厂。如何去配置。
稍微分析一下源码,这个工厂是什么,我们应该如何如给他赋值。

上面这个是RedisTemplate的set赋值方法,可见需要一个RedisConnectionFactory,这个又是什么呢?继续点进去。

很明显这是个接口,那我们要找他的实现类,

可见一共有两个实现类 JedisConnectionFactory 和 LettuceConnectionFactory 。

好的,有耐心分析到这说明你已经有分析源码的动力了!!!!!!!!!太深了咱也没必要挖。

接下来看我们如何去配置

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
}

这里不知道大家有没有疑问,为什么RedisConnectionFactory作为形参传过来,我们直接set就行,我们并没有给它创建对象,按理说它不应该是null吗?实际上这个方法是Spring自动调用的,参数呢也是Spring自动去到Spring的容器中寻找然后自动装配上去的,所以此处不为空,那我们怎么证明呢?直接打个断点去看。

此处我们可以直接看出它不是null,而且可以看出它默认使用的工厂LettuceConnectionFactory,
所以我们不用担心直接使用即可。

(3)我们配置完我们工厂后又会有新问题:乱码问题怎么解决。

首先我们去随便存储一个key-value到我们的Redis数据库中。代码:

@RestController
@RequestMapping("/test")
public class RedisController {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @RequestMapping("/aaa")
    public String testA(){
        redisTemplate.opsForValue().set("test","123456");
        return "success";
    }
}

结果

很明显,出现了奇怪的东?首先它并不是乱码,他只是存储的时候前面保存了序列化的元数据部分,跟它存储key-value的序列化方式有关,我们没有设置的情况下,它默认使用JdkserializationRedisSerializer,这个就会保留元数据,要想不保存元数据就需要改变它默认的序列化方式,其常用的有两种StringRedisSerializerJackson2RedisSerializer,这两种都不会保留序列的元数据,他们两个有什么区别的,咱直接试一下就知道。
假设先使用第一种StringRedisSerialzer,设置key和value都用这个。代码如下:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());//设置key存储的序列化方式
        redisTemplate.setValueSerializer(new StringRedisSerializer());//设置value存储的序列化方式
        return redisTemplate;
    }
}

我给它存储一个User对象。

@RestController
@RequestMapping("/test")
public class RedisController {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @RequestMapping("/aaa")
    public String testA(){
        User user = new User();
        user.setName("张三");
        user.setAge(22);
        redisTemplate.opsForValue().set("user",user);
        return "success";
    }
}

结果:

很显然,直接报错,说我们User不能被转换为String,因为我们设置value序列化方式为String。
接下来我们给它转换成JSON字符串进行存储试试看。引入阿里巴巴提供的FastJSON。

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>
@RestController
@RequestMapping("/test")
public class RedisController {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @RequestMapping("/aaa")
    public String testA(){
        User user = new User();
        user.setName("张三");
        user.setAge(22);
        redisTemplate.opsForValue().set("user", JSON.toJSONString(user));
        return "success";
    }
}

再来看结果:
这会就直接可以,但是它存进去的数据是JSON数据,并不是JSON字符串。

那么Jackson2RedisSerializer这个序列化方式的直接存储user对象,是可以直接存的不会报错,结果跟上面这个一样,但是如果你存JSON字符串进去,结果就是JSON字符串。结果:

一般来说JSON字符串都是带'\'这玩意的,当然上面也不是我们想要的,所以总结一下。

如果是StringRedisSerializer,传JSON字符串,如果是Jackson2JsonRedisSerializer,直接传对象。
 

虽然讲了上面两种的区别,但是往往这也是最烦人的,我喜欢唯一,所以二者必须要选一种,哪怕是公司的话也是选择一种,我们就选择StringRedisSerializer。
最终代码:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

4.真的需要RedisConfig吗?

虽然上面讲了那么多,但是,只是让大家了解一下原理,或者公司的写法可能就是这样的,但是为什么可以不用写这个呢?首先SpringBoot集成了Redis以后,会有一个RedisAutoConfiguration的自动配置类。

可以很明显的看出,它已经为我们提供了两个现成的RedisTemplate和StringRedisTemplate。

其中前者我们不可能用,因为之前讲过了,它没有设置序列化方式,会出现“乱码”问题。

那就需要自己配置也就是我们上面的3讲的。那为什么可用不用自己配置呢,这就是讲的StringRedisTemplate,它要求我们设置的key和value都必须是String类型,那为什么它不会出现乱码呢?我们直接点进去看一看。

可以从源码看出来它实际上是继承RedisTemplate的基础上在创建对象的时候又进行了序列化的设置,设置为RedisSerialiizer.string()。这个点进去实际上就是StringRedisSerializer.

所以我们上面的3实际上就是StringRedisTemplate。那公司开发时为什么还要自己配置呢?我觉得可能是沿用n多年前公司的写法,但是当时具体的含义又不是很清楚,所以到现在还保持着。

5.实际公司开发怎么选择?

如果是老项目,我们就没必要去纠结这个问题,我们知道它为什么这么写就行了,你不可能说给它改了,人家那种写法也不错,其实只要统一要求开发规范就行,大家都遵守这个进行开发。

如果是新项目,当然我们可以和leader去沟通这个问题,其实问题不大,只要你讲清楚就行。

第一种的话,它不会进行编译检查,如果你直接value是个对象而没有转换成JSON字符串,那么运行的时候直接会报错,这种就很不好。
第二种的话,我们就强制要求了,你传对象过来,我编译器就报错,你就必须给我转成JSON字符串进行存储,就不会出现上面的问题,这个规范就很强制。

我们知道上面的区别和好处啊,具体使用看情况,你别头铁不顾一切你就要用你自己的方式,到最后领导找你再让你改。但是我们一定要知道,分清楚。

其次就是我们一般还会封装一个Redis的工具类,虽然set存储其实也就一句话,还是封装一下更统一开发规范和要求。更高的提升效率。

6.封装Redis的工具类RedisUtil

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return 陈工失败
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                Boolean boo = redisTemplate.expire(key, time, TimeUnit.SECONDS);
                return boo != null && boo;
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            Boolean boo = redisTemplate.hasKey(key);
            return boo != null && boo;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    public boolean del(String... key) {
        try {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
                   redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));

                }
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据前缀删除key
     * @param prex
     */
    public boolean deleteByPrex(String prex) {
        try {
            prex = prex+"**";
            Set<String> keys = redisTemplate.keys(prex);
            redisTemplate.delete(keys);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

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

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        try{
            return redisTemplate.opsForValue().get(key);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

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

    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return 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;
        }
    }

    /**
     * 加锁
     *
     * @param key         键
     * @param value         值
     * @param releaseTime 锁过期时间 秒
     * @return 结果
     */
    public boolean lock(String key, Object value, long releaseTime) {
        try {
            Boolean boo = redisTemplate.opsForValue().setIfAbsent(key, value, releaseTime, TimeUnit.SECONDS);
            return boo != null && boo;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 解锁
     *
     * @param key key
     */
    public boolean unLock(String key) {
        try {
            Boolean boo = redisTemplate.delete(key);
            return boo != null && boo;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 向指定list的队列头部批量添加value
     *
     * @param key key
     */
    public boolean rightPush(String key, String value) {
        try {
            Long aLong = redisTemplate.opsForList().rightPush(key, value);
            return aLong != 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 向指定list的队列头部批量添加value
     *
     * @param key key
     */
    public boolean leftPush(String key, String value) {
        try {
            Long aLong = redisTemplate.opsForList().leftPush(key, value);
            return aLong != 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 移除并获取指定list中(队列-头部/尾部)第一个元素
     *
     * @param key key
     */
    public Object leftPop(String key) {
        try {
            return redisTemplate.opsForList().leftPop(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }


    /**
     * 指定key+!
     *
     * @param key key
     */
    public Object incr(String key,Long num) {
        try {
            return redisTemplate.opsForValue().increment(key,num);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    /**
     * 根据参数查询相关KEY集合
     */
    public Set<String> getKeyListByStr(String key){
        key = key+"**";
        return redisTemplate.keys(key);
    }


}

虽然有一些简答的没必要,但是一切都是为了统一开发规范,我们给类加个@Compinent注解,让我们的@Autowired注解生效,其次我们可以直接通过@Autowired注入RedisUtil进行方法调用。

@RestController
@RequestMapping("/test")
public class RedisController {
    @Autowired
    private RedisUtil redisUtil;
    @RequestMapping("/aaa")
    public String testA(){
        User user = new User();
        user.setName("张三");
        user.setAge(22);
        redisUtil.set("test","8888888888888");
        return "success";
    }
}

有点累了,后续出常用的操作及其应用场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1973362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VBA字典与数组第十七讲:工作表数组大小的扩展及意义

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

JAVA(IO流)7.31

ok了家人们今天还是学习IO流&#xff0c; 一.打印流【了解】 1.1 打印流的概述 我们平时使用的System语句就是调用了print()方法和println()方法。 这两个方法都来自于 java.io.PrintStream 类。 作用&#xff1a; 该类能够方便地打印各种数据类型的值&#xff0c;写入数据后…

谷粒商城实战笔记-115-全文检索-ElasticSearch-进阶-bool复合查询

文章目录 1&#xff0c;must2&#xff0c;must not3&#xff0c;should 1&#xff0c;must {"query": {"bool": {"must": [{"match": {"gender": "M"}},{"match": {"address": "mill&q…

java代码审计-SQL的注入

0x01 前言 Java里面常见的数据库连接方式有三种&#xff0c;分别是JDBC&#xff0c;Mybatis&#xff0c;和Hibernate。 0x02 JDBC注入场景 很早之前的Javaweb都是用JDBC的方式连接数据库然后去实现dao接口再调service业务层去实现功能代码JDBC连接代码 WebServlet("/d…

科技云报道:大模型引领技术浪潮,AI安全治理面临“大考”

科技云报道原创。 从文生文到文生图&#xff0c;再到文生视频&#xff0c;近年来&#xff0c;以ChatGPT、Sora等为代表的大模型引领了全球人工智能技术与产业的新一轮浪潮。2024年更是被业内称为大模型应用爆发元年。 年初&#xff0c;Sora横空出世验证了Scalling Law在视频生…

计算机的错误计算(五十)

摘要 扩展了计算机的错误计算&#xff08;四十九&#xff09;中的代码。同时发现&#xff0c;误差也“扩展”了。 下面是代码&#xff1a; import torch# 设置随机种子 torch.manual_seed(0)# 创建张量并移动到GPU W1 torch.randn(5, 3) * 10 W1 W1.to(cuda) X1 torch.ran…

高级宏定义

平时常说的 C 语言三大预处理功能是什么&#xff1f;&#xff08;吹牛谈资&#xff0c;不能不知&#xff09; 答&#xff1a;宏定义&#xff1b;文件包含&#xff1b;条件编译。 说到底&#xff0c;宏定义的实质是什么&#xff1f; 答&#xff1a;替换。 关于宏定义有一点…

CSS技巧专栏:一日一例 18 -纯CSS实现背景浮光掠影的按钮特效

CSS技巧专栏:一日一例 18 -纯CSS实现背景浮光掠影的按钮特效 先发图,再说话: 案例图片 案例分析 按钮是好几种颜色的背景色组成的,使用css的话,应该会有几个不同颜色的层,在按钮后面移动。每个层互相叠加,大概还会用到图片混合模式产生了更多的叠加的颜色,然后边缘过…

云计算实训20——mysql数据库安装及应用(增、删、改、查)

一、mysql安装基本步骤 1.下载安装包 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 2.解压 tar -xf mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 3.卸载mariadb yum -y remove mariadb 查看解压后的包 [rootmysq…

二叉树遍历算法的应用

1、二叉树的创建 2、二叉树的复制 3、二叉树的深度 4、计算结点总个数

jsp 自定义taglib

一、简介 我们在javaWeb开发中&#xff0c;经常会用到jsp的taglib标签&#xff0c;有时候并不能满足我们的实际需要&#xff0c;这就需要我们自定义taglib标签&#xff0c; 二、开发步骤 1、编写control方法&#xff0c;继承BodyTagSupport 2、定义zdytaglib.tld标签文件 3、…

AI Agent 如何入门?来看看这本新书!!!

半个月前&#xff0c;粗心的我细心地发现&#xff0c;有一本关于 Agent 的书籍&#xff0c;作者还是熟悉的咖哥&#xff08;黄佳老师&#xff0c;当年拜读过他的《零基础学机器学习》&#xff09;。 而在昨天&#xff0c;我终于收到了&#xff01;立刻花了半个小时品读起来~觉…

LeetCode 572.另一棵树的子树 C写法

LeetCode 572.另一棵树的子树 C写法 思路&#x1f9d0;&#xff1a; 可以用上判断两棵树是否相同的方法&#xff0c;root的每个结点都去与subroot进行该方法的比较&#xff0c;如果有一轮比较成功就表示root包含subroot。 代码&#x1f50e;&#xff1a; bool isSameTree(struc…

【时时三省】(C语言基础)函数的声明和定义

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ——csdn时时三省 函数声明 1.告诉编译器有一个函数叫什么&#xff0c;参数是什么&#xff0c;返回类型是什么。但是具体是不是存在&#xff0c;无关紧要。 2.函数的声明一般出现在函数的使用之前。要满足先声…

视频监控汇聚平台LntonCVS安防管理平台构建互联网视频监控安全管理系统方案

随着互联网和人工智能技术的发展&#xff0c;我们致力于建设一种云端平台&#xff0c;统一整合各类二三类视频资源&#xff0c;实现对网络工程最后100米、10米甚至1米的全域覆盖。 依托互联网与VPN网络&#xff0c;我们提供了一种视频监控安全管理系统方案&#xff0c;集成了大…

SpringSecurity+Mysql数据库实现用户安全登录认证

Spring Security 是一个提供身份认证、授权和防范常见攻击的安全权限框架。无论是对命令式&#xff0c;还是响应式web应用程序都完美支持&#xff0c;现在主要用作保护基于 Spring 框架的应用程序的事实标准。相对于shiro来说&#xff0c;SpringSecurity功能更加复杂而且更加强…

深入浅出:可视化理解揭示决策树与梯度提升背后的数学原理

决策树是一种非参数的监督学习算法&#xff0c;可用于分类和回归。它使用类似树的结构来表示决策及其潜在结果。决策树易于理解和解释&#xff0c;并且可以轻松地进行可视化。但是当决策树模型变得过于复杂时&#xff0c;它不能很好地从训练数据中泛化&#xff0c;会导致过拟合…

64、ELK记录nginx、httpd、mysqld日志

logstash收集服务日志文件 一、logstash收集appache日志文件&#xff0c;转发到elasticsearch 1、建立在elk1、2、3搭建好elk架构基础之上192.168.168.61 es1 2/4g192.168.168.62 es2 2/4g192.168.168.63 logstash kibana nginx/http 4/8g三台开启服务systemctl s…

《数据结构》(C语言版)第1章 绪论(上)

第1章 绪论 1.1 数据结构的研究内容1.2 基本概念和术语 1.1 数据结构的研究内容 N.沃思&#xff08;Niklaus Wirth)教授提出&#xff1a; 程序算法数据结构 电子计算机的主要用途 早期&#xff1a;主要用于数值计算 后来&#xff1a;非数值计算&#xff0c;复杂的具有一定结构…

Gstreamer配合srs服务器实现RTMP直播和WebRtc直播

前言 上一篇文章,实现了配合腾讯云直播的推流,但是需要花钱,于是就在思考能不能搞一个局域网内,免费的RTMP直播呢? 最终发现了可以使用srs服务器。如果成功了,以后也可以使用webrtc的直播推流。 以下是实现效果: 一、搭建srs服务器: 参考:ubuntu环境下搭建SRS服务器(…