Redis实战篇一 (短信登录)

2023-11-18


本期学习路线

短信登陆:  Redis的共享session应用
商户查询缓存:  企业的缓存使用技巧;缓存雪崩、穿透等问题解决
达人探店:  基于List的点赞列表;基于SortedSet的点赞排行榜
优惠券秒杀:  Redis计数器、Lua脚本Redis;分布式锁;Redis的三种消息队列
好友关注:  基于Set集合的关注、取关、共同关注、消息推送等功能
附近商户功能:  Redis的GeoHash的应用
用户签到:  Redis的BitMap数据统计功能
UV统计:  Redis的HyperLogLog的统计功能

项目整体架构

采用前后端分离部署,前端部署到Nginx服务器中,后端部署到Tomcat服务器中; 移动端/PC端发送请求时,首先向nginx发起页面请求,页面资源通过ajax向服务端发起请求查询数据。将查询到的数据后返回前端,前端再做页面渲染即可。

在这里插入图片描述
项目资源
链接:https://pan.baidu.com/s/11kh42hq6QWFm5PtBvT2MHQ
提取码:GY66

项目部署

后端部署

将模块导入后,修改配置文件(application.yml),启动项目后,在浏览器访问:http://localhost:8081/shop-type/list,若访问数据成功则证明部署成功
在这里插入图片描述

前端部署

运行前端项目,在nginx所在目录下打开一个CMD窗口,输入命令:

start   nginx.exe

在这里插入图片描述

打开浏览器,在空白页面点击鼠标右键,选择检查,打开开发者工具,然后打开手机模式(前端项目模拟手机app形式);访问:http://localhost:8080,即可看到页面:
在这里插入图片描述

短信登陆

基于Session实现登录

在这里插入图片描述

发送短信验证码

说明
请求方式 POST
请求路径 /user/code
请求参数 phone:电话号码
返回值
    @Override
    public Result sendCode(String phone, HttpSession session) {
        // 1.校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            // 2.不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3.符合,生成验证码--->6位随机数
        String code = RandomUtil.randomNumbers(6);
        // 4.保存验证码到session
        session.setAttribute("code",code);
        // 5.发送验证码--->需调用第三方平台,这里记录日志(@Slf4j)模拟发送成功
        log.debug("发送短信验证码成功,验证码:{}",code);
        // 6.返回ok
        return Result.ok();
    }
}

重启服务
在这里插入图片描述

后台接收验证码成功
在这里插入图片描述

短信验证码登录

说明
请求方式 POST
请求路径 /user/login
请求参数 phone:电话号码; code:验证码
返回值
  @Override
    public Result login(LoginFormDTO loginForm,HttpSession session) {
        String phone = loginForm.getPhone();
        // 1.校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            // 1.2 不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 2.校验验证码
        Object cacheCode = session.getAttribute("code");
        String code = loginForm.getCode();
        if(cacheCode==null|| !cacheCode.toString().equals(code)){
            // 3.不一致,报错
            return Result.fail("验证码错误");
        }
        // 4.一致,根据手机号查询用户
        User user = query().eq("phone", phone).one();
        // 5.判断用户是否存在
        if (user == null) {
            // 6.不存在,创建新用户并保存
           user= createUser(phone);
        }
        // 7. 保存用户信息到session
        session.setAttribute("user",user);
        return Result.ok();
    }
    private User createUser(String phone) {
        // 1.创建用户(电话号,昵称)
        User user=new User(phone, RandomUtil.randomString(5));
        // 2.保存用户
        save(user);
        return user;
    }

为什么采用反向校验?
此种编码校验不需要多层if嵌套,若都为正向验证代码则不够优雅

登录验证功能(登录校验拦截器)
在这里插入图片描述
编写拦截器类

package com.hmdp.utils;


import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//拦截器
public class LoginInterceptor implements HandlerInterceptor {
    // 前置拦截器--->进入controller之前做登录校验
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 1.获取session
        HttpSession session = request.getSession();
    // 2.获取session中的用户       .
        Object user = session.getAttribute("user");
        // 3.判断用户是否存在
        if (user == null) {
            // 4.不存在,拦截, 返回401状态码--->未授权
            response.setStatus(401);
           return false; 
        }
    // 5.存在,保存用户信息到ThreadLocal
        UserHolder.saveUser( user);
    // 6.放行    
        return true;
    }
    // 渲染之后(返回给用户之前)--->销毁用户信息,避免内存泄露
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserHolder.removeUser();
    }
}

拦截器的生效(在config)

package com.hmdp.config;

import com.hmdp.utils.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class MvcConfig  implements WebMvcConfigurer {

    // 添加拦截器(实现addInterceptors方法)
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // excludePathPatterns("")-->排除不需要拦截的路径
     registry.addInterceptor(new LoginInterceptor())
             .excludePathPatterns("shop/**",
                                  "shop-type/**",
                                  "upload/**",
                                  "/blog/hot",
                                  "/user/code",
                                  "/user/login");
    }
}

实现controller层登录校验功能

 @GetMapping("/me")
    public Result me(){
        // TODO 获取当前登录的用户并返回
        User user= UserHolder.getUser();
        return Result.ok(user);
    }

重启服务测试
在这里插入图片描述
隐藏用户敏感信息
登录校验功能返回用户id、昵称、头像等信息即可。像时间、密码、电话等敏感信息存在泄露风险,无需返回,

在这里插入图片描述

  // 7.保存用户到session中  (将保存进session中的用户信息修改为UserDTO类型)
        session.setAttribute("user", BeanUtil.copyProperties(user,UserDTO.class));
        return Result.ok();

重启服务查看用户信息,此时避免返回用户敏感信息,也可减少内存的占用(只剩下三个字段)
在这里插入图片描述

集群的Session共享问题

session共享问题:多台Tomcat并不共享session存储空间,当请求切换到不同Tomcat服务时会导致数据丢失的问题。
session的替代方案应该满足
● 数据共享
● 内存存储
● key、value结构
在这里插入图片描述

基于Redis实现共享session登录

在这里插入图片描述
在这里插入图片描述

保存登录的用户信息,可以使用String结构,以JSON字符串来保存,比较直观:

KEY VALUE
SSS:user:1 { name:“Jack”,age:21" }
SSS:user:2 { name:“Rose”,age:18" }

Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD,并且内存占用更少:
在这里插入图片描述

业务层实现类

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {


   @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    // 发送短信验证码
    public Result sendcode(String phone, HttpSession session) {
        //  1. 校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3.符合,生成验证码--->6位随机数
        String code = RandomUtil.randomNumbers(6);
        // 4.保存验证码到 Redis 并且设置有效期(给KEY添加一个业务前缀加以区分,防止与其他业务的KEY产生冲突,KEY也具有层次感)

        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);

        // 5.发送验证码--->记录日志,模拟发送成功
        log.debug("发送短信验证码成功,验证码{}",code);
        // 返回ok
        return  Result.ok();
    }
    // 短信验证码登录、注册
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        String phone = loginForm.getPhone();
        // 1.校验手机号
        if(RegexUtils.isPhoneInvalid(phone)){
            // 1.2 如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 2. 从Redis中获取验证码并校验
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if (cacheCode==null||!cacheCode.equals(code)){
            // 3.不一致,报错
            return Result.fail("验证码错误");
        }
        // 4.一致,根据手机号查询用户
        User user=query().eq("phone",phone).one();
        // 5.判断用户是否存在
        if(user==null){
            // 6.不存在,创建新用户并保存
         user=createUser(phone);
        }

        /* 7.保存用户信息到Redis中 (用Hash结构存储)*/
        // 7.1 随机生成token,作为登录令牌
        String token = UUID.randomUUID().toString(true);
        // 7.2 将User对象转为Hash存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
        // 7.3 存储
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,userMap);
        // 7.4 设置token有效期  (若登录后不做任何操作,30分钟后中从内存中清除--->避免内存占用过多)
        stringRedisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL,TimeUnit.MINUTES);

        // 8 返回token
        return Result.ok(token);
    }

    private User createUser(String phone) {
        // 1.创建用户
        User user=new User(phone,"GY_"+RandomUtil.randomString(4));
        // 2.保存用户
        save(user);
        return user;
    }
}

登录拦截器类

//拦截器
public class LoginInterceptor implements HandlerInterceptor {


    private StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    // 前置拦截器--->进入controller之前做登录校验
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 1. 获取请求头中的token
        String token = request.getHeader("authorization");
        if(StrUtil.isBlank(token)){
            // 不存在,拦截, 返回401状态码--->未授权
            response.setStatus(401);
        }
        // 2. 基于token获取Redis中的用户       .
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
        // 3.判断用户是否存在
        if (userMap.isEmpty()) {
            // 4.不存在,拦截, 返回401状态码--->未授权
            response.setStatus(401);
           return false;
        }
    // 5. 将查询到的Hash数据转换为UserDTO对象    【islgnoreError:是否忽略转换过程中的错误】
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        // 6. 存在,保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);
    // 7. 刷新token有效期
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
    // 8.放行
        return true;
    }
    // 渲染之后(返回给用户之前)--->销毁用户信息,避免内存泄露
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserHolder.removeUser();
    }
}

拦截器实现类

@Configuration
public class MvcConfig  implements WebMvcConfigurer {

   @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 添加拦截器(实现addInterceptors方法)
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // excludePathPatterns("")-->排除不需要拦截的路径
     registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
             .excludePathPatterns("shop/**",
                                  "shop-type/**",
                                  "upload/**",
                                  "/blog/hot",
                                  "/user/code",
                                  "/user/login");
    }
}

登录成功!!!
在这里插入图片描述

解决状态登录刷新的问题——登录拦截器的优化

在这里插入图片描述

token刷新拦截器类
刷新token有效期,并将用户保存到ThreadLocal

// token刷新拦截器
public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    // 前置拦截器--->进入controller之前做登录校验
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 1. 获取请求头中的token
        String token = request.getHeader("authorization");
        if(StrUtil.isBlank(token)){
           return true;
        }
        // 2. 基于token获取Redis中的用户       .
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
        // 3.判断用户是否存在
        if (userMap.isEmpty()) {
            return true;
        }
    // 5. 将查询到的Hash数据转换为UserDTO对象    【islgnoreError:是否忽略转换过程中的错误】
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        // 6. 存在,保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);
    // 7. 刷新token有效期
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
    // 8.放行
        return true;
    }
}

登录拦截器
从RefreshTokenInterceptor拦截器保存的ThreadLocal中查询用户

// 登录拦截器
public class LoginInterceptor implements HandlerInterceptor {


    // 前置拦截器--->进入controller之前做登录校验
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.判断是否需要拦截(ThreadLocal中是否有用户)
        if (UserHolder.getUser() == null) {
            //  没有,需要拦截,设置状态码  【401: 未登录】
            response.setStatus(401);
            //  拦截
            return false;
        }
        //  有用户,则放行
        return true;
    }
}

拦截器实现类

@Configuration
public class MvcConfig  implements WebMvcConfigurer {
   @Resource
    private StringRedisTemplate stringRedisTemplate;
    // 登录拦截器(拦截部分请求)
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // excludePathPatterns("")-->排除不需要拦截的路径
     registry.addInterceptor(new LoginInterceptor())
             .excludePathPatterns("shop/**",
                                  "shop-type/**",
                                  "upload/**",
                                  "/blog/hot",
                                  "/user/code",
                                  "/user/login").order(1);
     // token刷新拦截器(默认拦截所有请求)
     registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis实战篇一 (短信登录) 的相关文章

  • 如何在由子控件组成的 SWT 复合材料上跟踪鼠标?

    我创建了自己的控件 我想跟踪鼠标并添加一个MouseTrackListener 很遗憾MouseEnter and MouseLeave当鼠标移动到我的合成部分 即标签和按钮 上时 也会生成事件 Mouse enter mouse ente
  • 如何在 JavaFX 中连接可观察列表?

    我所说的串联是指获得一个新列表 该列表侦听所有串联部分的更改 方法的目的是什么FXCollections concat ObservableList
  • 两个整数乘积的模

    我必须找到c c a b mod m a b c m 是 32 位整数 但 a b 可以超过 32 位 我正在尝试找出一种计算 c 的方法 而不使用 long 或任何 gt 32 位的数据类型 有任何想法吗 如果m是质数 事情可以简化吗 注
  • 如何在 JPQL 或 HQL 中进行限制查询?

    在 Hibernate 3 中 有没有办法在 HQL 中执行相当于以下 MySQL 限制的操作 select from a table order by a table column desc limit 0 20 如果可能的话 我不想使用
  • Android studio - 如何保存先前活动中选择的数据

    这是我的代码片段 这Textview充当按钮并具有Onclicklistner在他们 当cpu1000时Textview单击它会导致cpu g1000其代码如下所示的类 public class Game 1000 extends AppC
  • 提供节点名或服务名,或未知 Java

    最近我尝试运行我的 Java 项目 每当我运行它并将其打开到我得到的服务器地址时 Unable to determine host name java net UnknownHostException Caused by java net
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • 在 Java 中如何找出哪个对象打开了文件?

    我需要找出答案哪个对象在我的 Java 应用程序中打开了一个文件 这是为了调试 因此欢迎使用工具或实用程序 如果发现哪个对象太具体了 这class也会很有帮助 这可能很棘手 您可以从使用分析器开始 例如VisualVM http visua
  • 如何检查某个元素是否存在于一组项目中?

    In an ifJava中的语句如何检查一个对象是否存在于一组项目中 例如 在这种情况下 我需要验证水果是苹果 橙子还是香蕉 if fruitname in APPLE ORANGES GRAPES Do something 这是一件非常微
  • Java 中如何将 char 转换为 int? [复制]

    这个问题在这里已经有答案了 我是Java编程新手 我有例如 char x 9 我需要得到撇号中的数字 即数字 9 本身 我尝试执行以下操作 char x 9 int y int x 但没有成功 那么我应该怎么做才能得到撇号中的数字呢 ASC
  • Java Applet 中的 Apache FOP - 未找到数据的 ImagePreloader

    我正在研究成熟商业产品中的一个问题 简而言之 我们使用 Apache POI 库的一部分来读取 Word DOC 或 DOCX 文件 并将其转换为 XSL FO 以便我们可以进行标记替换 然后 我们使用嵌入到 Java 程序中的 FOP 将
  • 从jar中获取资源

    我有包含文件的 jar myJar res endingRule txt myJar wordcalculator merger Marge class 在 Marge java 中我有代码 private static final Str
  • Java继承,扩展类如何影响实际类

    我正在查看 Sun 认证学习指南 其中有一段描述了最终修饰符 它说 如果程序员可以自由地扩展我们所知的 String 类文明 它可能会崩溃 他什么意思 如果可以扩展 String 类 我是否不会有一个名为 MyString 的类继承所有 S
  • JDBC 时间戳和日期 GMT 问题

    我有一个 JDBC 日期列 如果我使用 getDate 则会得到 date 仅部分2009 年 10 月 2 日但如果我使用 getTimestamp 我会得到完整的 date 2009 年 10 月 2 日 13 56 78 890 这正
  • 为什么\0在java中不同系统中打印不同的输出

    下面的代码在不同的系统中打印不同的输出 String s hello vsrd replace 0 System out println s 当我在我的系统中尝试时 Linux Ubuntu Netbeans 7 1 它打印 When I
  • 列表过滤器内的 Java 8 lambda 列表

    示例 JSON id 1 products id 333 status Active id 222 status Inactive id 111 status Active id 2 products id 6 status Active
  • Log4j2 ThreadContext 映射不适用于parallelStream()

    我有以下示例代码 public class Test static System setProperty isThreadContextMapInheritable true private static final Logger LOGG
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • Java RMI - 客户端超时

    我正在使用 Java RMI 构建分布式系统 它必须支持服务器丢失 如果我的客户端使用 RMI 连接到服务器 如果该服务器出现故障 例如电缆问题 我的客户端应该会收到异常 以便它可以连接到其他服务器 但是当服务器出现故障时 我的客户端什么也
  • 抛出 Java 异常时是否会生成堆栈跟踪?

    这是假设我们不调用 printstacktrace 方法 只是抛出和捕获 我们正在考虑这样做是为了解决一些性能瓶颈 不 堆栈跟踪是在构造异常对象时生成的 而不是在抛出异常对象时生成的 Throwable 构造函数调用 fillInStack

随机推荐

  • mysql中如何操作varchar类型的日期进行比较、排序等操作

    在mysql使用过程中 日期一般都是以datetime timestamp等格式进行存储的 但有时会因为特殊的需求或历史原因 日期的存储格式是varchar 那么我们该如何处理这个varchar格式的日期数据呢 使用函数 STR TO DA
  • SSM框架基于JSP犬舍寄养系统

    项目介绍 SSM框架基于JSP犬舍寄养系统的设计与实现 高清视频演示 SSM框架基于JSP犬舍寄养系统的设计与实现 安装视频演示 SSM框架基于JSP犬舍寄养系统的设计与实现 系统说明 1 前台功能模块 首先注册会员 登录进平台 然后选择自
  • 【PyTorch学习】(三)自定义Datasets

    torchvision datasets源码地址 https github com pytorch vision blob master torchvision datasets 前两篇从搭建经典的ResNet DenseNet入手简单的了
  • 【LVGL 学习】样式(style)风格学习

    概述 在 LVGL 中 样式都是以对象的方式存在 一个对象可以描述一种样式 每个控件都可以独立添加样式 创建的样式之间互不影响 可以使用 lv style t 类型创建一个样式并初始化 static lv style t style lv
  • 数据结构算法:写一个递归算法来实现字符串的逆序存放

    题目要求 写一个递归算法来实现字符串的逆序存储 要求不另设存储空间 首先我们定一个一个存放字符串的结构体 typedef struct String ElemType data int length String PString 创建字符串
  • 解决IE浏览器报错,对象不支持“assign”属性或方法

    报错页面 报错代码 解决后 解决代码 function doTest if typeof Object assign function Object assign function target use strict if target n
  • 线性回归分析

    文章目录 一 高尔顿数据集进行线性回归分析 1 1 父母平均身高和儿子身高线性回归分析 1 2 父亲身高和儿子身高线性回归分析 1 3 母亲身高和儿子身高线性回归分析 二 Anscombe四重奏数据集进行线性回归分析 一 高尔顿数据集进行线
  • MFC读取Excel(一)

    软件 vs2013 程序功能 MFC读取Excel里的第一个单元格的值 步骤 第一步 创建基于对话框的MFC工程 第二步 添加库 添加Excel类库 在工程名上右键 选择 添加 类 或者点击菜单栏的 项目 gt 添加类 选择 TypeLib
  • Windows下常用的快捷方式

    罗列出windows下我最常用的快捷键 逐步补充 打开我的电脑 win e
  • Kubesphere部署三高商城组织架构说明

    KubeSphere部署三高商城组织架构说明 一 创建企业空间 1 使用ws manager用户登录KubeSphere web控制器 创建企业空间 2 登出控制台 然后以 ws admin 身份重新登录 在企业空间设置中 选择企业空间成员
  • uniapp开发支付宝小程序之上传小程序

    市面上很多关于微信小程序通过uinapp开发的文档 支付宝的文档较少 这里做一下补充 为后浪提供参考 一给窝里giaogiao 通过hbuilder编码小程序后不能直接在支付宝开发者工具中上传 应该先通过HBuilder编译一下在操作 步骤
  • ERP中HR模块的操作与设计--开源软件诞生26

    赤龙ERP的EHR功能讲解 第26篇 用日志记录 开源软件 的诞生 进入地址 点亮星星 祈盼着一个鼓励 博主开源地址 码云 https gitee com redragon redragon erp GitHub https github
  • 51单片机——ADC模数转换、DAC数模转换PWM C语言入门编程

    目录 ADC XPT2046 1 ADC模数转换 数码管上显示AD模块采集电位器的阻值 热敏的温度值 光敏的光值 DAC PWM 1 DAC数模转换 DAC PWM 模块上的指示灯DA1呈呼吸灯效果 由暗变亮再由亮变暗 ADC ADC an
  • 聊聊技术专家谈阿里云史诗级故障

    序言 什么是技术专家 其实也是很懂 是做的时间足够长呢 还是说经历的厂比较多 还是说纸上谈兵比较牛逼 专家嘛 大家都懂的 只会弹别人 喔 是谈别人 原来不是弹 有本事技术专家谈谈自己呗 风言风语 阿里云出现史诗级故障 处理的时间足够长 然后
  • 过去式加ed的发音_「初中英语语法大全」不规则动词过去式和过去分词巧记方法...

    动词的过去式和过去分词是初中英语教学中的重点 而有些动词的不规则变化是这些重点中的难点 但这些不规则变化也不是毫无规律可循的 现将初中英语中一些常用的不规则动词变化介绍如下 一 原形 过去式和过去分词的词形和读音都相同的单词 结尾字母一般是
  • 计算机视觉项目实战(一)、图像滤波和图像混合 Image Filtering and Hybrid Images

    图像滤波和图像混合 Image Filtering and Hybrid Images 项目要求 项目原理 主要函数 my imfilter 函数解释 输入参数 输出参数 主要实现步骤 gen hybrid image 函数解释 输入参数
  • java中JSONArray 遍历方式

    第一种 java8 遍历JSONArray 拼接字符串 public static void main String args JSONArray jSONArray new JSONArray JSONObject jb new JSON
  • Linux中退出编辑模式的命令

    vi 文件 回车后就进入进入编辑模式 按 o 进行编辑 编辑结束 shift 退出编辑模式 然后输入退出命令 1 保存不退出 w 保存文件但不退出vi 编辑 w 强制保存 不退出vi 编辑 w file 将修改另存到file中 不退出vi
  • CNN可视化技术 -- CAM & Grad-CAM详解及pytorch简洁实现

    文章目录 前言 1 CAM Class Activation Map 2 Grad CAM 3 PyTorch中的hook机制 4 Grad CAM的PyTorch简洁实现 参考资料 前言 CNN中的特征可视化大体可分为两类 细节信息 ZF
  • Redis实战篇一 (短信登录)

    Redis企业实战 黑马点评 项目整体架构 项目部署 后端部署 前端部署 短信登陆 基于Session实现登录 集群的Session共享问题 基于Redis实现共享session登录 解决状态登录刷新的问题 登录拦截器的优化 本期学习路线