SpringCache

2020-12-29  本文已影响0人  Godlike_4029

JSR107

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

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 注解 简化我们开发

执行原理:每次调用需要缓存功能的方法时,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
上一篇下一篇

猜你喜欢

热点阅读