当前位置:

首页 > 编程开发 > Redis+SpringBoot案例分析

Redis+SpringBoot案例分析

Redis Assistant
Redis Assistant

Redis Assistant 是一款为Redis设计的高效可视化管理与监控工具,提供多种操作模式,高效处理服务器数据。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

立即下载
¥19
Windows/macOS/Linux 2026-08-22
正版软件 Windows macOS软件 Linux 数据库管理工具

一、项目环境前端技术栈:Vue-Cli前端软体:WebStorm2020.3前端样式:Bootstrap后端技术栈:SpringBoot后端软体:IntelliJIEDA2019JavaJDK:1.8服务器:阿里云Centos7其他:MyBatis,Redis,MySql,Docker,Shiro二、项目演示项目源码:shoppingProject01_pub:version6.0项目参考:Project05;不良人_Vue-Cli;不良人_Redis;不良人_Axios;尚硅谷_Redis项目功能:1)

本文目录

    一、项目环境

    1. 前端技术栈:Vue-Cli

    2. 前端软体:WebStorm 2020.3

    3. 前端样式: Bootstrap

    4. 后端技术栈:SpringBoot

    5. 后端软体:IntelliJ IEDA2019

    6. JavaJDK:1.8

    7. 服务器:阿里云Centos 7

    8. 其他:MyBatis,Redis,MySql,Docker,Shiro

    二、项目演示

    1. 项目源码:shoppingProject01_pub : version6.0

    2. 项目参考:Project05;不良人_Vue-Cli;不良人_Redis;不良人_Axios;尚硅谷_Redis

    3. 项目功能:
      1)邮箱注册登录:
      用户应用邮箱注册点击提交后,网站会给用户发送邮件附带激活码链接,用户点击链接实现账号激活传送门。
      2)短信注册登录:
      用户应用手机号注册时,点击“获取验证码”按钮,手机会接收到网站发送的短信附带验证码。基于Redis实现了验证码有效期5分钟,每个手机号只能获取三次短信验证码传送门。
      3)alipay支付:
      通过下载Android版支付宝沙箱app用户可通过alipay扫码购买网站上的商品,后台MySql会记录该订单行为传送门,网页展示如图1所示。
      Redis+SpringBoot案例分析

    图1 商品展示页面

    4)用户小分级:
    当用户扫码购买年度VIP会员后,购买网站上的商品一律半价,后台MySql记录用户角色的变更。
    5)用户积分排行榜:
    用户购买商品会增加自己的积分,网页展示如图2所示。
    Redis+SpringBoot案例分析

    图2 排行榜展示页面
    1. 项目中遇到的大坑:
      1)邮件发送功能本地测试通过,服务器端测试Bug频出,解决办法。
      2)项目在服务器部署后,无法连接服务器上的Redis。解决办法:(1)将Redis在服务器部署而非Docker;(2)将Redis端口改为7000;(3)防火墙active状态下放行服务器和阿里云的7000端口;(4)修改Redis.conf文件。
      3)git上传本地源码到gitee,误操作导致本地源码被gitee上的旧代码覆盖,第二天才发现。解决办法:因为在服务器上留有源码的jar包,通过反编译工具jd_gui救回半条命。另外git上传文件参考传送门。

    三、主要模块说明

    1 Vue-Cli模块说明:

    1.1 Vue-Cli的概述:

    1)以前后端分离、单页面web应用(SPA)为特点,Vue-Cli可以创建一个Vue项目,这个项目存在脚手架规范。Vue-Cli的优势如下:
    (1) 基于脚手架规范的开发会变得很灵活。
    (2) Vue-Cli基于webpack构建并带有合理的默认配置,打包工具webpack能够聚合单页面和各种开发组件。
    (3) Vue-Cli是一个丰富的官方插件集合,继承了前端生态中最好的工具。

    2)安装过程:
    (1) 安装WebStorm(用于开发),安装node.js,安装vue-cli,安装axios(用于发起跨域请求),引入bootstrap样式。

    3)部署过程:

    npm run build               # 在WebStorm终端执行,生成dist文件夹
    docker pull nginx:1.19.10   # 不建议Vue-cli项目部署到tomcat,因为tomcat属于动态服务器,启动需要java环境,是为了解析动态语言jsp的;像纯静态的就部署到静态服务器nginx上。
    mkdir html                  # 为了做docker容器内外的数据卷映射
    mv dist/ html/
    docker run -p 80:80 --name nginx01 -d -v /root/html/dist/:/usr/share/nginx/html nginx:1.19.10  # 数据卷映射
    # 此时可访问  http://120.79.133.235:80/index.html

    4)Vue-Cli开发要点:
    (1)在WebStorm中,开发过程主要面向src文件,如图3所示:
    Redis+SpringBoot案例分析

    图3 WebStorm目录


    [1] 首先掌握路由(router)和组件(components[公有组件],views[私有组件]),组件就是一个个“页面”,组件建立后要向路由进行注册; [2] asserts封装了bootstrap样式,并在main.js中被导入; [3] 为了发送跨域请求,在utils中封装了axios实例,代码如下:

    import axios from 'axios'
    
    // 创建默认实例
    const instance = axios.create({
      baseURL: 'http://120.79.133.235:8989/eb',
      // timeout: 10000,
    });
    
    // 请求拦截器
    instance.interceptors.request.use(config=>{
      console.log("请求拦截器");
      return config;
    })
    // 响应拦截器
    instance.interceptors.response.use(response=>{
      console.log("响应拦截器");
      return response;
    }, err=>{
      console.log("响应出现错误时进入的拦截器");
    });
    
    // 暴露instance实例对象
    export default instance;

    在各组件中,对于后端的get和post请求方法如下:

    // Get请求
    // 向后端接口发当前页码,获取当前页面的商品List
    instance.get("/item/findAllItem?page="+this.page).then(res=>{
            that.items = res.data.items;
            that.totalPage = res.data.totalPage;
            that.page = res.data.page;
          });
    
    // Post请求
    // 向后端接口发送当前商品id和用户id,获取商品购买状态
    instance.post("/order/alipay/callback",{itemId:this.itemid,userId:this.user.id}).then(res=>{
            if ( res.data.code == 20000 ) {
              alert("提示:您已购买该商品");
            } else {
              alert("提示:您未购买该商品");
            }
          });
        }

    [4] 组件之间的跳转和传值方法如下:

    // 跳转到MailReg组件
    this.$router.push({name:"MailReg"});
    
    // 跳转到item组件,并传递当前商品的id
    this.$router.push({path:"/item",query:{ItemId:myid}});
    // item组件接收方法:
    this.itemid = this.$route.query.ItemId;
    
    // 另外不同组件可以依据token获取登录用户信息,需要用到redis,详见下文

    2 用户积分排行榜模块说明:
    1.1 Reids概述:
    1) Redis是一种基于内存的数据存储NoSql;
    2) Redis支持丰富的数据类型(String, List, Set, ZSet, Hash);
    3) Redis有两种持久化方法: (1)快照(snapshot)存储,也叫rdb持久化,保存当前时刻的数据状态;(2) AOF(append only file)存储,将所有redis的写命令记录到日志文件中,Redis支持持久化间隔最快也是一秒,所以它是事务不安全的,即是可能丢失数据的。
    4)Redis的应用场景:
    (1) 利用Redis字符串完成项目中手机验证码存储的实现。------本项目采用
    (2) 利用Redis字符串类型完成具有时效性的业务功能,如订单还有40分钟即将关闭。
    (3) 利用Redis实现分布式集群系统中的Session共享。
    (4) 利用Redis的ZSet数据类型(可排序set类型:元素+分数)实现排行榜功能。 ------本项目采用
    (5) 利用Redis完成分布式缓存。 ------本项目实现MySql中数据的缓存
    (6) 利用Redis存储认证之后的token信息。 ------非常方便,本项目采用。
    (7) 利用Redis解决分布式集群系统中分布式锁问题。

    1.2 基于Redis实现前端组件从后端获取用户信息:
    Step1:前端Login.vue组件中用户输入登录信息提交的接口如下:

    // 这里调用了后端/user/login接口,获取当前登录用户的token,存入Session的localStorage中,在后续网页浏览过程中可随时调取这个token
    instance.post("/user/login",this.user).then(res=>{
            if ( res.data.state ) {
              alert(res.data.msg+",点击确定进入主页");
              // 前端存储token信息
              localStorage.setItem("token",res.data.token);
              that.$router.push({path:"/itemList"});
            } else {
              alert(res.data.msg);
              that.user = {};
            }
          });

    Step2:后端/user/login接口实现如下:

    // Controller层
    @PostMapping("login")
    public Map loginAccount(@RequestBody User user, HttpSession session) {
        return userService.loginAccount(user, session);
    }
    
    // Service层
    // 情况3:查询到一个用户时
    // 获取主体对象
    try {
         Subject subject = SecurityUtils.getSubject();
         subject.login(new UsernamePasswordToken(user.getName(), user.getPassword()));
         User userDB = userListDB.get(0);
         String token = session.getId();
         if (userDB.getScore() == null) {
               userDB.setScore(0.0);
               userDAO.updateUserScore(userDB);
         }
         redisTemplate.opsForValue().set("TOKEN_" + token, userDB, 30, TimeUnit.MINUTES);
         redisTemplate.opsForZSet().add("userRank", userDB, userDB.getScore());
         map.put("token", token);
         map.put("state",true);
         map.put("msg","登录成功");
         return map;
         ...

    Redis整合SpringBoot有两种Template,即RedisTemplate和StringRedisTemplate。其中StringRedisTemplate是RedisTemplate的子类,两个方法基本一致,不同之处在于操作的数据类型不同,RedisTemplate中的两个泛型都是Object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的key和value都只能是字符串。
    在Step2中,我将token和数据库中的用户信息userDB绑定在一起存入了Redis中,后续前端组件获取登录用户信息的代码如下:

    // 从localStorage获取token
    let token = localStorage.getItem("token");
    let that = this;
    // 发送axios请求,根据token获取用户信息
    instance.get("/user/token?token="+token).then(res=>{
    that.user = res.data;
    console.log(that.user);
    })

    后端/user/token的接口如下:

    @GetMapping({"token"})
    public User findUser(String token) {
       System.out.println("接收的token信息:" + token);
       return (User)redisTemplate.opsForValue().get("TOKEN_" + token);
    }

    Step3:用户退出登录时,应消除浏览器中对应的token,后端接口代码如下:

        // 退出登录
        @DeleteMapping({"logout"})
        public Map logout(String token) {
            Map map = new HashMap<>();
            try {
                redisTemplate.delete("TOKEN_" + token);
                Subject subject = SecurityUtils.getSubject();
                subject.logout();
                map.put("state", true);
                map.put("msg", "提示:退出账户成功");
                return map;
            } catch (Exception e) {
                e.printStackTrace();
                map.put("state", false);
                map.put("msg", "提示:退出账户失败");
                return map;
            }
        }

    1.3 基于Redis的用户积分排行榜实现:
    MySql中的用户信息如图4所示:
    Redis+SpringBoot案例分析

    图4 MySql中的用户信息


    Redis中的UserRank如图5所示:

    Redis+SpringBoot案例分析

    图5 Redis中的UserRank

    Step1:当用户登录时,他的首要任务是接入UserRank对应的信息,后端代码如下:

    if (userDB.getScore() == null) {
        userDB.setScore(0.0);
        userDAO.updateUserScore(userDB);
    }
        redisTemplate.opsForValue().set("TOKEN_" + token, userDB, 30, TimeUnit.MINUTES);
        redisTemplate.opsForZSet().add("userRank", userDB, userDB.getScore());

    userDB是数据库中当前登录用户的信息(一定是有的,你注册了,对吧?),若用户首次登录我将他的分数在数据库设置为0.0,之后我在Redis的ZSet中加入这个用户,你知道,Set集合不会存储重复key值的元素,因此不会同一个用户出现在UserRank中两次。两个template完成了token绑定User,User绑定UserRank中Score的过程,之后的分数更新过程会反复使用这两个template实现。
    Step2:当用户信息更新时,相应的与用户信息有关的两个template都要发生变化,代码如下:

    // key值序列化
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    // 由当前用户的token获取当前用户的信息
    User firstUser = (User)redisTemplate.opsForValue().get("TOKEN_" + token);
    // 删除zSet中的当前用户
     redisTemplate.opsForZSet().remove("userRank", firstUser);
    // 产生新的当前用户(昵称改变)
    List userListDB = this.userDAO.selectUserByName(user.getName());
    User secondUser = userListDB.get(0);
    // 更新token中当前用户的信息
    redisTemplate.opsForValue().set("TOKEN_" + token, secondUser, 30, TimeUnit.MINUTES);
    // 产生zSet中的当前用户
    redisTemplate.opsForZSet().add("userRank", secondUser, secondUser.getScore());

    Step3:当用户扫码支付时,首次进入的后端controller如下:

    // 支付单件商品
        @GetMapping("payForItem")
        public byte[] alipay(String itemid,String userid, String token) {
            this.token = token;
            log.info("itemid=====>"+itemid);
            log.info("userid=====>"+userid);
            PayVo payVo = new PayVo();
            payVo.setItemId(itemid);
            payVo.setUserId(userid);
            System.out.println(payVo.getUserId());
            return alipayService.alipay(payVo);
        }

    在alipayService有一个小型用户分级,即vip用户购物价格减半:

                // 1:支付的用户
                String userId = payVo.getUserId();
                // my 1: 依据用户id查询用户
                User user = userService.selectUserById(Integer.valueOf(userId));
                // 依据商品id查询商品
                Item item = itemService.getItemById(payVo.getItemId());
                // my 1: 依据用户id查询用户
                if ( item == null ) return null;
                // 2: 支付金额
                String tempMoney = item.getPrice().toString();
                String money = "";
                if ( user.getRole().equals("normal") ) {
                    money = tempMoney;
                }
                if ( user.getRole().equals("vip") ) {
                    Double tempMoney2 = Double.valueOf(tempMoney)*0.5;
                    money = String.valueOf(tempMoney2);
                }

    在payForItem相同文件下,调用了payCommonService,在这里会实现用户积分更新和用户等级更新:

    payCommonService.payUserPublic(bodyJsonObject, userId, user.getName(), orderNumber, tradeno, "alipay", this.token);

    将"VIP"这件商品的id设置为“666”,当用户购买该商品时,当前用户更新过程如下:

    if ( itemId.equals("666") ) {
                int myuserId = Integer.valueOf(userId);
                User userDB = userService.selectUserById(myuserId);
                // key值序列化
                this.redisTemplate.setKeySerializer(new StringRedisSerializer());
                // 由当前token获取当前用户信息
                User firstUser = (User)redisTemplate.opsForValue().get("TOKEN_" + token);
                // 由当前用户信息删除当前用户zSet
                redisTemplate.opsForZSet().remove("userRank", firstUser);
                // 更新当前用户信息身份
                userDB.setRole("vip");
                // 更新当前用户新身份的分数
                userService.updateUserRole(userDB);
                List userListDB = this.userDAO.selectUserByName(userDB.getName());
                // 获取当前新身份用户的完整信息
                User secondUser = userListDB.get(0);
                // 更新当前token对应的当前用户
                redisTemplate.opsForValue().set("TOKEN_" + token, secondUser, 30, TimeUnit.MINUTES);
                // 设置当前用户的zSet
                redisTemplate.opsForZSet().add("userRank", secondUser, secondUser.getScore().doubleValue());
            }

    当前用户积分更新过程如下:

            // 更新当前用户的积分
            double tempScore = Double.valueOf(orderDetail.getPrice()) * 0.3;
            String key1 = "TOKEN_" + token;
            // 由当前token获取当前用户
            User user = (User)redisTemplate.opsForValue().get(key1);
            // 更新当前用户的zSet分数
            redisTemplate.opsForZSet().incrementScore("userRank", user, tempScore);
            // 获取当前用户的zSet分数
            double newScore = redisTemplate.opsForZSet().score("userRank", user);
            // 删除当前用户的zSet(因为要更新当前用户的信息,将当前用户在数据库中的分数进行同步)
            redisTemplate.opsForZSet().remove("userRank", new Object[] { user });
            user.setScore(newScore);
            userDAO.updateUserScore(user);
            // 更新token对应的当前用户的信息
            redisTemplate.opsForValue().set(key1, user);
            // 新增当前用户的zSet
            redisTemplate.opsForZSet().add("userRank", user, newScore);
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    php72w redis,docker php7安装php-redis
    php72w redis,docker php7安装php-redis

    在Docker环境下为PHP安装Redis扩展:手动编译需下载源码、安装phpize、配置编译并安装,生成redis.so并在php.d目录新建redis.ini加载,重启PHP-FPM;也可直接yum安装php72w-pecl-redis,免手动编译。

    php 如何配置 redis,php 如何设置redis
    php 如何配置 redis,php 如何设置redis

    配置PHP的Redis扩展:先安装Redis服务,从GitHub下载phpredis源码,用phpize编译安装,修改php.ini添加extension=redis.so,重启php-fpm或Apache,通过phpinfo验证并连接Redis。

    c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】
    c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】

    C++结构体无法直接序列化为RESP协议,需手动将字段映射为*N数组嵌套$lenbulkstring形式。整数需转换为字符串并加:前缀,空字符串用$0\r\n,null用$-1\r\n。长度计算需精确到字节数,特别注意二进制安全,同时确保字段顺序与结构体定义一致。

    如何在Golang架构中部署Redict作为高能数据临时存储中转站
    如何在Golang架构中部署Redict作为高能数据临时存储中转站

    Redis作为跨服务临时状态枢纽,需按消息队列、缓存和原子状态三重角色设计。选型用List实现任务分发及延迟重试,Stream用于事件广播与位点回溯。go-redis/v8需配置MinIdleConns、MaxRetries及超时。数据写入应JSON序列化并带type标签、设合理TTL、将关键字段提至顶层。消费通过原子GET+DEL或processedkey

    TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制
    TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制

    在高并发秒杀场景下,利用Redis的减库存原子操作预扣减库存拦截超卖请求,再通过数据库乐观锁(版本号与库存校验)确保最终一致性,异常时回滚Redis库存并借助Lua脚本保证原子性,从而有效防止库存超卖。

    如何在 Go 中利用 go-redis 执行 Lua 脚本优化逻辑
    如何在 Go 中利用 go-redis 执行 Lua 脚本优化逻辑

    在Go中使用go-redis执行Lua脚本可借助原子性解决竞态问题,通过redis.Script封装脚本并自动处理SHA1缓存与重试。需注意KEYS必须为非空切片且所有键在Cluster模式下落在同一slot,ARGV以字符串传入并由tonumber转换。返回值需先检查错误再调用Val(),避免panic。

    TP6.0 基于 Redis SetNX 实现的简单互斥锁机制【并发】
    TP6.0 基于 Redis SetNX 实现的简单互斥锁机制【并发】

    先看一个典型场景:电商秒杀、库存扣减、定时任务重复调度——这些并发冲突在 TP6 应用里屡见不鲜。用 Redis 的 SETNX 做互斥锁,确实是轻量又直接的方案,但“能用”和“稳妥”之间,隔着三层硬功夫:原子加锁、身份校验、合理超时。这三条,差一条就翻车。 很多人第一次上手,直接写 SETNX 加

    TP6.0 如何处理高并发抢购?Redis 原子锁与队列削峰详解【秒杀】
    TP6.0 如何处理高并发抢购?Redis 原子锁与队列削峰详解【秒杀】

    高并发抢购中,Redis的DECR原子操作可安全扣库存;配合think-queue异步削峰,接口仅扣库存和推消息;分布式锁需用SETNXEX加唯一标识,释放时用Lua脚本校验;成功资格后须以数据库事务双写为准,失败时补回Redis库存。

    Golang 实现基于 Redis 的延时任务队列
    Golang 实现基于 Redis 的延时任务队列

    基于Redis的ZSET实现延时队列需解决并发重复消费、失败重试混乱、精度不足及内存爆炸等问题。通过Lua脚本保证取删原子性,使用毫秒级时间戳,对临时错误采用指数退避重试,永久错误记入独立哈希表,并限制最大延迟、定期清理过期任务、使用紧凑序列化格式。

    RedisInsight怎么修改Key的过期时间 Redis操作方法【步骤】
    RedisInsight怎么修改Key的过期时间 Redis操作方法【步骤】

    操作 Redis 的时候,改个 Key 的过期时间其实是个高频场景。特别是做运维排查或者临时调试那会儿,谁也不想反复切命令行窗口、背那些命令参数。好在 RedisInsight 这个图形工具把这事儿做得很顺手——三种方式任选,总有一种合你胃口。 在动手前,先确认一下 Key 当前的 TTL 状态是个

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    即将离开本站
    您即将前往第三方网站,请确认是否继续?