SpringBoot内置缓存


​ 以后做系统开发其实主要做的就是信息处理,而当我们读取数据时,如果应用程序直接与数据库打交道的话,受数据库的访问效率的影响,系统整体性能偏低。为了改善上述现象,开发者通常会在应用程序与数据库之间建立一种临时的数据存储机制,该区域中的数据在内存中保存,读写速度较快,可以有效解决数据库访问效率低下的问题。这一块临时存储数据的区域就是缓存。

​ 缓存是什么?缓存是一种介于数据永久存储介质与应用程序之间的数据临时存储介质,使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能。此外缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间。而 springboot 提供了对市面上几乎所有的缓存技术进行整合的方案,在学习 springboot 整合其他缓存技术之前,我们先一起学习如何使用 springboot 的内置缓存。

SpringBoot 内置缓存解决方案

​ springboot 技术提供有内置的缓存解决方案,可以帮助开发者快速开启缓存技术,并使用缓存技术进行数据的快速操作,如读取缓存数据和写入数据到缓存。内置缓存实现如下:

步骤①:导入 springboot 提供的缓存技术对应的starter

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

步骤②:启用缓存,在 springboot 程序引导类上方标注注解 @EnableCaching

@SpringBootApplication
// 开启缓存功能
@EnableCaching
public class Springboot12CacheApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(Springboot12CacheApplication.class, args);
    }

}

步骤③:设置操作的数据是否使用缓存

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    @Cacheable(value="cacheSpace",key="#id")
    public Book getById(Integer id) {
        return bookDao.selectById(id);
    }
}

​ 上例中 value 属性描述缓存的存储位置,可以理解为是一个存储空间名,key 属性描述了缓存中保存数据的名称,使用 #id 读取形参中的 id 值作为缓存名称。使用 @Cacheable 注解后,执行当前操作,如果发现对应名称在缓存中没有数据,就正常读取数据,然后放入缓存;如果对应名称在缓存中有数据,就终止当前业务方法执行,直接返回缓存中的数据。

​ 这一过程可以用 HashMap 开启一个缓存实现,具体代码如下:

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;
    
    private HashMap<Integer, Book> cache = new HashMap<>();

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

手机验证码案例

​ 在学习其他缓存技术之前,我们先用springboot内置缓存实现一个手机验证码的案例,模拟使用缓存保存手机验证码的过程。手机验证码需求如下:

  • 输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)
  • 输入手机号和验证码验证结果

为了实现这个案例,我们需要制作一个接口,接口中定义两个方法,由对应的实现类实现。其中一个方法是用来模拟发送短信的过程,其实就是根据用户提供的手机号生成一个验证码,然后放入缓存,另一个用来模拟验证码校验过程,并返回校验的结果。下面是制作该案例的模拟代码:

​ 前面两个步骤和前面一样,首先导入 springboot 提供缓存技术对应的 starter,然后启用缓存,这里就不作叙述。

步骤③:制作验证码对应的实体类,封装手机号与验证码两个属性

@Data
public class SMSCode {
    private String tele;
    private String code;
}

步骤④:定义本案例的业务层接口与实现类

public interface SMSCodeService {
    public String sendCodeToSMS(String tele);
    public boolean checkCode(SMSCode smsCode);
}

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    @Autowired
    private CodeUtils codeUtils;

    @CachePut(value = "smsCode", key = "#tele")
    public String sendCodeToSMS(String tele) {
        String code = codeUtils.generator(tele);
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
        //取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
        String code = smsCode.getCode();
        String cacheCode = codeUtils.get(smsCode.getTele());
        return code.equals(cacheCode);
    }
}

​ 注意这里不能用 @Cacheable 注解,因为 @Cacheable 注解是缓存中没有值则放入值,缓存中有值则取值。而验证码有一定的时效性,当验证码失效时必须重新获取验证码,所以这个注解不能用,应该使用仅具有向缓存中保存数据的功能,这里使用 @CachePut 注解即可。

​ 这里生成验证码和校验验证码的功能建议放入专门的工具类中实现。

步骤⑤:定义验证码的生成策略与根据手机号读取验证码的功能

@Component
public class CodeUtils {
    private String [] patch = {"000000","00000","0000","000","00","0",""};

    public String generator(String tele){
        int hash = tele.hashCode();
        int encryption = 20206666;
        long result = hash ^ encryption;
        long nowTime = System.currentTimeMillis();
        result = result ^ nowTime;
        long code = result % 1000000;
        code = code < 0 ? -code : code;
        String codeStr = code + "";
        int len = codeStr.length();
        return patch[len] + codeStr;
    }

    @Cacheable(value = "smsCode",key="#tele")
    public String get(String tele){
        return null;
    }
}

步骤⑥:定义验证码功能的web层接口,一个方法用于提供手机号获取验证码,一个方法用于提供手机号和验证码进行校验

@RestController
@RequestMapping("/sms")
public class SMSCodeController {
    @Autowired
    private SMSCodeService smsCodeService;
    
    @GetMapping
    public String getCode(String tele){
        String code = smsCodeService.sendCodeToSMS(tele);
        return code;
    }
    
    @PostMapping
    public boolean checkCode(SMSCode smsCode){
        return smsCodeService.checkCode(smsCode);
    }
}

文章作者: 不才叶某
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 不才叶某 !
评论
  目录