【开发篇】十、Spring缓存:手机验证码的生成与校验

文章目录

1、缓存

  • 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
  • 使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能
  • 缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间

注意最后这条,缓存的不一定就是从持久层数据库来的数据,也可以是程序运行的临时数据,理解别太狭义,如手机验证码,对应于下图的Cache1:

2、用HashMap模拟自定义缓存

传统的一个查询接口,每次都查需要去和数据库交互,数据库压力大且容易产生性能瓶颈:


在Service层引入一个Map类型的非局部变量,来模拟缓存:(这种使用一个Map来充当临时缓冲池的思想需要学习)

java 复制代码
private HashMap<Integer,Book> cache = new HashMap<>();

@Override
public Book getBookById(Integer id){

	Book book = cache.get(id);
	
	if(book == null){
		book = bookDao.selectById(id);
		cache.put(id,book);
	}
	
	return book;
}

此时调用之前的接口,除了第一次需要查数据库,后面直到服务重启,变量被回收,都不用再去查数据库。同理,写个手机验证码的demo代码:

看下效果:


3、SpringBoot提供缓存的使用

首先导入缓存技术对应的starter:

xml 复制代码
<dependency>    
	<groupId>org.springframework.boot</groupId>    
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

配置配或者直接启动类上加@EnableCaching启动缓存

java 复制代码
@SpringBootApplication

@EnableCaching  //!!!!

public class SpringbootApplication {    

	public static void main(String[] args) {
	
		SpringApplication.run(SpringbootApplication.class, args);    
		
	}
}

此时,上面用HashMap模拟缓存的Service码就可改为:

java 复制代码
@Cacheable(value="cacheSpace",key="#id")

public Book getById(Integer id) {    

	return bookDao.selectById(id);
	
}

即先从cacheSpace这块缓存空间查看,有则返回,没有再查持久层。@Cacheable注解,即以属性里的key值为键,以方法的返回值为value,既存又取,有则取,无则查后存。 以上是背后使用的缓存技术是SpringBoot默认的Simple。SpringBoot提供的缓存技术除了提供默认的缓存方案外,还可以对其他缓存技术进行整合,统一接口,方便缓存技术的开发与管理:

  • Generic
  • JCache
  • Ehcache
  • Hazelcast
  • Infinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple(默认)
  • memcached

4、手机验证码案例完善

引入@Cacheable注解后,重写并完善下这个验证码的案例。先写个工具类来生成验证码,这个工具类写的不优雅,重点备份下补0串的这种思想吧:

java 复制代码
public class CodeUtil {

    private static final String[] patch = {"000000","00000","0000","000","00","0",""};

    public static String generatorCode(String tel){
        int hash = tel.hashCode();
        int encryption = 20230927;  //加密常量码
        long result = hash ^ encryption;  //第一次加密,此时,我加密码写死,同一个电话号码的验证码会一直不变
        long nowTime = System.currentTimeMillis();
        result = result ^ nowTime;  //引入时间,二次加密
        long code = Math.abs(result % 1000000);  //取后六位
        String codeStr = code + "";  //也可能不够6位,比如000147,则上面的long code就是147
        return patch[codeStr.length()] + codeStr;  //加一个补0数组,根据字符串长度来取对应的补0串,最多补5个0,最少不补,这里为了适配数组下标从0开始,给array[0]给个值
    }
}

注意根据字符串长度来取数组中对应的补0串时,长度为6,则取array[6],但数组下标从0开始,会越界:

此时有两种思路处理,一种是上面的,给数组加个下标为0的值,此时str.length()就和数组下标对应上了,也可以直接让str.length()-1:

java 复制代码
return patch[codeStr.length() -1 ] + codeStr;

Service层,生成验证码,并存缓存,不能用@Cacheable,它既存又取,这样验证码在缓存失效前都一样,可改为@CachePut只存不取

java 复制代码
@Service
@Slf4j
public class MsgServiceImpl implements MsgService {

    @Resource
    @Lazy  //解决下循环以来的问题
    MsgService msgService;

    /**
     * 返回验证码
     * @param tel
     * @return
     */
    @Override
    //@Cacheable(value = "telCode",key = "#tel")
    @CachePut(value = "telCode",key = "#tel")
    public String getCheckCode(String tel) {
        String checkCode = CodeUtil.generatorCode(tel);
        return checkCode;
    }

    /**
     * 校验验证码
     * @param tel
     * @param checkCode
     * @return
     */
    @Override
    public boolean verify(String tel, String checkCode) {

        return checkCode.equals(msgService.getCode(tel));
    }

    @Override
    @Cacheable(value = "telCode",key = "#tel")
    public String getCode(String tel){
        return null;
    }
}

关于校验方法里的取验证码,之前写需求用redis,直接redisTemplate.get了,但这里底层缓存技术是Simple,可单独写个方法,上面加@Caceable注解,然后校验方法里调用这个获取缓存值的方法即可实现(此时会有@Cacheable注解失效问题,注意出去绕一圈拿代理对象来调用)。还是利用了@Cacheable注解的特点,先查有无这个key,有就把value当作这个方法的返回值,没有再执行代码方法体。

java 复制代码
@Cacheable(value = "telCode",key = "#tel")
public String getCode(String tel){
    return null;
}

Controller层随便写就行:

java 复制代码
@RestController
@RequestMapping("/msg")
public class MsgController {

    @Resource
    MsgService msgService;

    @GetMapping("{tel}")
    public String getCode(@PathVariable String tel){
        return msgService.getCheckCode(tel);
    }

    @PostMapping
    public Boolean verifyCode(@RequestParam String tel,String code){
        return msgService.verify(tel,code);
    }
}

重点关注:

  • 工具类中加密使用了一个补0数组的思想
  • @Cacheable既存又取,这样验证码在缓存失效前都一样,可改为@CachePut只存不取
  • @Cacheable注解因方法内部调用而失效的解决
  • 不用Redis,再取数据时,引入了一个方法,加@Cacheable注解并返回null,即查到这个key的值就返回,否则返回方法的返回值,即null
相关推荐
冰帝海岸18 分钟前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象44 分钟前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了1 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·1 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic2 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王2 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康2 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神2 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_327342733 小时前
Java实现离线身份证号码OCR识别
java·开发语言
Oak Zhang3 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存