• SpringBoot系列(十五)整合缓存,项目会用得到的技术


    一、缓存有什么用?

     缓存应该是我们每一个系统都应该考虑使用的,这样可以加速系统的访问,提升系统的性能。比如我们经常需要访问的高频数据,将此类数据放在缓存中,可以大幅度提升系统的响应速度。原因就是一般来说我们的数据都是存在数据库中,但是高频的访问数据库不仅会对数据库造成压力,一定程度上还会影响响应速度,影响用户体验。如果引入了缓存,不仅能提升访问性能的同时降低数据库的压力。

    二、JSR107规范

     JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

    规范:

     JCache规范定义了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等,可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快15倍)。

     Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。其中CacheProvider就是缓存提供器,其中包含了多个缓存管理器(CacheManager),缓存管理器之下才是我们需要用到的缓存(Cache),缓存的形式就是键值对,即entry对象(Entry),每一个entry对象会对应一个Expiry,即可以对每一条缓存数据设置有效期。

    ​ 上面都是抄过来的废话,看下面,实际操作一下。

    三、Spring缓存抽象

    Spring 3.1开始定义了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发。SpringBoot对缓存整合提供了很好的支持,支持不同的缓存规范(所谓的规范就是相当于面向接口编程,不同的缓存有不同的实现)。

    其中Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
    Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;下面我们来看看,SpringBoot中默认的一些缓存配置与实现。默认的是SimpleCache。

    四、SpringBoot中的缓存-注解开发

    新建SpringBoot web项目,加入依赖配置:SpringBoot版本为2.2.6.RELEASE 库存文章了,版本是去年的。

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

    然后我们逐个介绍一下缓存开发中用到的一些注解。

    • @EnableCaching
    • @Cacheable
    • @CachePut
    • @CacheEvict
    • @Caching
    • @CacheConfig

    1.@EnableCaching

     这个注解应该是最先了解的,把它放在启动类上面,表示开启基于注解的缓存,然后你用那些缓存相关的注解就能被Spring容器扫描到。这个注解不能缺,不然项目会报错。

    @EnableCaching
    @SpringBootApplication
    public class DemoCacheApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoCacheApplication.class, args);
        }
    }
    

    2.@Cacheable

     这个注解有很多的属性,可以定义一些缓存的规则。主要是用于方法之上,将方法的结果作为缓存的值,默认情况下将方法的参数作为缓存的键(key),也就是key-value的形式存储数据。首先介绍一下这个注解的运行机制:首先在方法运行之前,先根据注解配置的value或者是cacheNames到cacheManage中去查询缓存组件即Cache,当然如果第一次没有这个缓存就会自动创建。获取到缓存组件之后就用我们设置的key属性,或者是keyGenerator策略生成的key去查询键对应的缓存值,对应上面的entry对象。如果查到了值就返回给请求客户端,如果没有查到这个值就调用方法,然后将方法返回的结果缓存起来。

    这里我们来说一说这个注解里面的一些属性的用法:

    • value/cacheNames:这两个属性是一样的,都是指定一个缓存组件的名字,当然这个可以指定多个,是一个数组。
    • key/keyGenerator:这两个有一定的区别,但是用的时候只需要用其中一个就行了。key是我们可以直接指定这个缓存结果对应的key是什么,到时候直接去Cache里面查就行了,keyGenerator就是可以自定义一个key的生成策略,然后生成key,两者从结果上来看都是生成了key,所以两者只需要用其中一个就OK了。
    • condition:在符合这个条件下才进行缓存
    • unless:当这个条件为假的时候才进行缓存,与condition相反

    当然还有cacheManager(缓存管理器)以及cacheResolver(缓存解析器),这两个都可以自己定义。两个都差不多,二者用其中一个就OK。

    下面我们来看看SpEl写法,因为我们的key或者是condition,或者是unless属性都会用到这个写法。

    来自官方的spel 语法

    Name Location Description Example
    methodName Root object 被调用方法的名称 #root.methodName
    method Root object 被调用的方法 #root.method.name
    target Root object 被调用的目标对象 #root.target
    targetClass Root object 被调用目标的类 #root.targetClass
    args Root object 用于调用目标的参数(作为数组) #root.args[0]
    caches Root object 运行当前方法的缓存集合 #root.caches[0].name
    Argument name Evaluation context 任何方法参数的名称。如果名称不可用(可能是由于没有调试信息),则参数名称也可在代表参数索引的#a<#arg> where下#arg(从 开始0)。 #iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias).
    result Evaluation context 方法调用的结果(要缓存的值)。仅在unless 表达式、cache put表达式(用于计算key)或cache evict 表达式(何时beforeInvocationfalse)中可用。对于支持的包装器(例如 Optional),#result指的是实际对象,而不是包装器。 #result

    具体使用如下:

    @GetMapping("/cacheAble")
    @Cacheable(value = "cache",key = "#root.args[0]",condition = "#id>20")
    public  Student cacheAble(@RequestParam Integer id) {
        Student student = (Student) hashMap.get(id);
        System.out.println("第一次调用了方法,添加缓存");
        return student;
    }
    

    上面就相当于是将第一个方法参数id作为缓存的key ,返回值Student 作为值。缓存的名称就是cache,设置在参数id大于20的时候才进行缓存,unless 则是相反的。

    3.@CachePut

    这个注解是用来更新缓存的,一般情况下都是用在更新数据的接口上,示例如下:

    @PostMapping("/putCache")
    @CachePut(value = "cache",key = "#result.id")
    public Student putCache(@RequestBody Student student) {
        hashMap.replace(student.getId(),student);
        System.out.println("调用了方法,也更新了缓存");
        return student;
    }
    

    4.@CacheEvict

    删除缓存的注解,可以设置删除全部缓存还是删除部分缓存数据。如下

    @CacheEvict(value = "cache",key = "#id",beforeInvocation = true,allEntries = false)
    @GetMapping("/cacheEvict")
    public void cacheEvict(@RequestParam Integer id) {
        hashMap.remove(id);
        System.out.println("缓存删除");
    }
    

    beforeInvocation 设置为true ,表示在方法执行之前进行缓存删除,默认为false,``allEntries`设置为true ,表示删除这个缓存名称下面的所有缓存。

    5.@Caching

    借助官方的话说,有时,需要指定多个相同类型的注解(例如@CacheEvict@CachePut)——例如,因为不同缓存之间的条件或键表达式不同。@Caching允许在同一方法上使用多个嵌套的 @Cacheable@CachePut@CacheEvict注释。实际上就是,各个缓存注解可以配合使用,操作不同的缓存空间。

     @Caching(
         cacheable = {
             @Cacheable(value = "cache",key = "#id"),
             @Cacheable(value = "cache1",key = "#id")
         },
         evict = {@CacheEvict(value = "cache2",key = "#id")}
     )
    @GetMapping("/caching")
    public Student caching(@RequestParam Integer id) {
        Student student = (Student)hashMap.get(id);
        System.out.println("新增缓存了,也删除了缓存,但是操作的cache不一样");
        return student;
    }
    

    代码实现的就是将数据缓存到cache,cache1,再将cache2的缓存删除掉。

    6.@CacheConfig

    这是一个类级别的缓存配置注解,像缓存名称,缓存处理器(cacheResolver),缓存管理器(cacheManager),这些配置可以直接配置到类上面,这样方法对应的就能少些一些重复代码。

    //注解的四个属性
    String[] cacheNames() default {};
    
    String keyGenerator() default "";
    
    String cacheManager() default "";
     
    String cacheResolver() default "";
    

    示例: 在类上面,加上注解,配置好对应的缓存名称,key生成策略等,keyGenerator,cacheManager,cacheResolver这三个东西留着下篇文章将redis缓存的时候讲,主要是比较多。

    @CacheConfig(cacheNames = "chache")
    public class CacheController {}
    

    确实,我们的缓存还能从cacheManager中获取,这很好理解,cacheManager是管理缓存的,当然也能获取缓存了。

    @GetMapping("/get")
    public Student getCache(@RequestParam Integer id){
        Cache cache = cacheManager.getCache("cache");
        Cache.ValueWrapper student = cache.get(id);
        Object o = student.get();
        return (Student) o;
    }
    

    由于SpringBoot的良好扩展性,CacheManager,CacheResolver,
    KeyGenerator都可以自己配置,具体的下一篇文章会介绍。

    心诚不灭
  • 相关阅读:
    sencha touch 扩展篇之将sencha touch打包成安装程序(上)- 使用sencha cmd打包安装程序
    sencha touch 扩展篇之使用sass自定义主题样式 (下)通过css修改官方组件样式以及自定义图标
    一个不错的android组件的网站
    sencha touch 扩展篇之使用sass自定义主题样式 (上)使用官方的api修改主题样式
    sencha touch 入门系列 (九) sencha touch 布局layout
    面试题总结
    国外接活网站Elance, Freelancer和ScriptLance的介绍和对比
    sencha touch 入门系列 扩展篇之sencha touch 项目打包压缩
    Android Design Support Library——Navigation View
    设计模式——命令模式
  • 原文地址:https://www.cnblogs.com/swzx-1213/p/14974863.html
Copyright © 2020-2023  润新知