美文网首页
SpringCache

SpringCache

作者: Godlike_4029 | 来源:发表于2020-12-29 20:06 被阅读0次

    JSR107

    Java Caching 定义了5个核心接口 分别是 CachingProvider,CachManager、Cache、Entry、Expiry。

    • CachingProvider 定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期间访问多CachingProvider。
    • CacheManger 定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache 存在于CacheManager 的上下文中,一个CacheManager 仅被一个CacheingProvider 所拥有。
    • Cache 是一个类似Map的数据结构并临时存储以key 为索引的值。一个Cache 仅被一个CacheManager 所拥有
    • Entry 是一个存储在Cache中的key-value对
    • Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个有效期,条目变为过期状态。一旦过期,条目将不可访问、更新、和删除。缓存有效期可以通过 ExpiryPolicy 设置。
    image.png

    开发中使用JSR-107需要导入包:一般不直接使用JSR-107开发,因为JSR-107仅仅定义了接口,而没有实现

    <!-- https://mvnrepository.com/artifact/javax.cache/cache-api -->
    <dependency>
        <groupId>javax.cache</groupId>
        <artifactId>cache-api</artifactId>
        <version>1.1.0</version>
    </dependency>
    

    Spring缓存抽象

    Spring 从3.1 开始支持 JSR-107 注解 简化我们开发

    • org.springframework.cache.Cache
    • org.springframework.cache.CacheManager
    • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合
    • Cache接口下Spring提供了各种xxxCache的实现;如RedisCache、EhCacheCache、ConcurrentMapCache等;


      image.png

    执行原理:每次调用需要缓存功能的方法时,Spring会检查指定参数的目标方法是否已经被调用过,如果有直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果返回给用户。下次调用直接从缓存中获取。

    使用Spring缓存抽象时我们需要关注以下两点:

    1. 确定方法需要被缓存以及他们都缓存策略
    2. 从缓存中读取之前都缓存存储都数据

    Spring 缓存开发重要概念

    Spring 缓存开发相关概念 作用功能
    Cache 缓存接口,定义缓存操作。实现有 如RedisCache、EhCacheCache、ConcurrentMapCache等
    CacheManager 缓存管理器,管理各种缓存(Cache)组件
    @Cacheable 主要针对方法配置,能够根据方法都请求参数对其结果进行缓存
    @CacheEvict 清空缓存
    @CachePut 保证方法被调用,又希望结果被缓存
    @EnableCaching 开启基于注解的缓存
    KeyGenerator 缓存数据时Key生成策略
    serilalize 缓存数据时value序列化策略

    SpringBoot 整合 Spring缓存抽象

    基于 spring-boot-starter 2.4.0
    springCache官方文档:https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/integration.html#cache

    搭建Mybatis crud 环境(略)

    1. 导入相关 pom文件
      <!--    Spring 缓存    -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-cache</artifactId>
       </dependency>
    
    1. 开启基于缓存的注解 @EnableCaching

    2. 标注缓存注解 @Cacheable(cacheNames = {"xxx"}

        @Cacheable(cacheNames = {"stu"})
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    

    如果只标注 @Cacheable 会报异常: At least one cache should be provided per cache operation.

    @Cacheable

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Cacheable {
    
        @AliasFor("cacheNames")
        String[] value() default {};
    
        @AliasFor("value")
        String[] cacheNames() default {};
    
        String key() default "";
    
        String cacheManager() default "";
    
        String cacheResolver() default "";
    
        String condition() default "";
    
        String unless() default "";
    
        boolean sync() default false;
    
    

    cacheNames/value :指定缓存组件对名字;CacheManager 管理多个Cache 组件,每一个缓存组件有自己唯一一个名字

    @Cacheable(cacheNames = {"stu"})
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    

    key:缓存数据的 key,默认是使用方法的参数的值。

    比如:使用方法名字getStu和方法参数拼接 key:getStu[1],编写SpEl:#id; 参数id的值 #a0 #p0 # root.args[0]

        @Cacheable(cacheNames = {"stu"}, key = "#root.methodName+'['+#id+']'")
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    

    debug模式下可以看下如下结果:


    image.png image.png

    keyGenerator:key的生成器,可以自己指定key的生成器的组件id

    编写配置类

    @Component(value = "MyKeyGenerator")
    public class MyKeyGenerator implements KeyGenerator{
        @Override
        public Object generate(Object target, Method method, Object... params) {
           return  method.getName()+"["+ Arrays.asList(params).toString()+"]";
        }
    }
    

    keyGenerator = "MyKeyGenerator"

        @Cacheable(cacheNames = {"stu"}, keyGenerator = "MyKeyGenerator")
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    

    cacheManager:指定缓存管理器

    cacheResolver:指定缓存解析器

    condition :指定符合条件的情况下才缓存

    id为1时才缓存

        @Cacheable(cacheNames = {"student"}, keyGenerator = "MyKeyGenerator", condition = "#a0 == 1")
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    

    unless :否定缓存:当unless 指定的条件为true,方法的返回值就不会被缓存;

        @Cacheable(cacheNames = {"stu"}, keyGenerator = "MyKeyGenerator", condition = "#a0 == 1", unless = "#a0 == 1")
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    

    注意:满足condition条件 又满足 unless 是不缓存的 unless 优先

    sync: 是否使用异步模式默认是false 如果开启为true 则unless 属性就不能得到支持

    可以获取 到结果进行判断 unless = “#result == null”

    @CachePut

    既调用方法又更新缓存。修改了数据库某个数据,同时更新缓存

    1. 先调用目标方法
    2. 将目标方法的结果缓存起来
      @CachePut注解中的属性和 @Cacheable中的属性几乎是一样的就不在展开
        @CachePut(value = "stu", key = "#student.id")
        @Override
        public Student updateStu(Student student){
            System.out.println(student);
            studentMapper.updateByPrimaryKey(student);
            return student;
        }
    

    key还可以写成 key = "#result.id",因为先调用方法然后把结果缓存起来,所以可以拿到结果取出id

    注意: 一定要指定key 不然会按默认值方法的参数 新生成一个 k,并把结果缓存

    @CacheEvict

    allEntries 默认 false 是否清空所有缓存
    beforeInvocation 默认 false 缓存清除操作在方法之后执行,如果方法异常,则缓存不会清除

    @CacheEvict(value = "#id", allEntries = true, beforeInvocation = true)
        public void delSut(Integer id) {
            System.out.println(id);
            studentMapper.deleteByPrimaryKey(id);
        }
    

    @Caching

    组合多个Cache注解使用

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Caching {
    
        Cacheable[] cacheable() default {};
    
        CachePut[] put() default {};
    
        CacheEvict[] evict() default {};
    
    }
    

    例子:

        @Caching(
                cacheable = {@Cacheable(value = "stu",key = "#userName")},
                put = {@CachePut(value = "stu", key = "#result.id"),
                        @CachePut(value = "stu", key = "#result.age")
                }
        )
        public Student getStuByStr(String userName) {
            StudentExample studentExample = new StudentExample();
            studentExample.createCriteria().andUserNameEqualTo(userName);
            List<Student> students = studentMapper.selectByExample(studentExample);
            return Optional.ofNullable(students).orElse(null).get(0);
        }
    
    @CacheConfig 抽取缓存的公共配置

    我们每个缓存注解中 都指定 了value = "stu" / cacheNames="stu" ,可以抽离出来,在整个类上添加
    @CacheConfig(cacheNames = "stu"),之后每个方法中都默认使用 cacheNames = “stu”

    @CacheConfig(cacheNames = "stu")
    @Service
    public class StudentServiceImpl implements StudentService {
    
        @Resource
        private StudentMapper studentMapper;
    
        @CachePut(key = "#result.id")
        @Override
        public Student updateStu(Student student){
            System.out.println(student);
            studentMapper.updateByPrimaryKey(student);
            return student;
        }
        /**
         * Cacheable
         * @param id
         * @return
         *
         * key = "#root.methodName+'['+#id+']'"
         */
        @Cacheable(key = "#id")
        @Override
        public Student getStu(Integer id) {
            Student student = studentMapper.selectByPrimaryKey(id);
            System.out.println(student);
            return student;
        }
    
        @CacheEvict(allEntries = true, beforeInvocation = true)
        public void delSut(Integer id) {
            System.out.println(id);
            studentMapper.deleteByPrimaryKey(id);
        }
    
        @Caching(
                cacheable = {@Cacheable(key = "#userName")},
                put = {@CachePut(key = "#result.id"),
                        @CachePut(key = "#result.age")
                }
        )
        public Student getStuByStr(String userName) {
            StudentExample studentExample = new StudentExample();
            studentExample.createCriteria().andUserNameEqualTo(userName);
            List<Student> students = studentMapper.selectByExample(studentExample);
            return Optional.ofNullable(students).orElse(null).get(0);
        }
    }
    

    Cache SpEL available metadata

    名字 位置 描述 实例
    methodName root对象 当前被调用的方法名 #root.methodname
    method root对象 当前被调用的方法 #root.method.name
    target root对象 当前被调用的目标对象实例 #root.target
    targetClass root对象 当前被调用的目标对象的类 #root.targetClass
    args root对象 当前被调用的方法的参数列表 #root.args[0],#a0,#p0
    caches root对象 当前方法调用使用的缓存列表 如 如@Cacheable(value={"cache1", "cache2"})),则有两个cache #root.caches[0].name
    Argument Name 执行上下文evaluator context 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引; #artsian.id #a0 #p0
    result 执行上下文evaluator context 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) #result

    相关文章

      网友评论

          本文标题:SpringCache

          本文链接:https://www.haomeiwen.com/subject/pfetoktx.html