乐趣区

关于spring:Spring系列缓存注解Cacheable-CacheEvit-CachePut-使用姿势介绍

SpringBoot 系列缓存注解 @Cacheable @CacheEvit @CachePut 应用姿态介绍

Spring 在 3.1 版本,就提供了一条基于注解的缓存策略,理论应用起来还是很丝滑的,本文将针对几个罕用的注解进行简略的介绍阐明,有须要的小伙伴能够尝试一下

本文次要知识点:

  • @Cacheable: 缓存存在,则应用缓存;不存在,则执行办法,并将后果塞入缓存
  • @CacheEvit: 生效缓存
  • @CachePut: 更新缓存

<!– more –>

I. 我的项目环境

1. 我的项目依赖

本我的项目借助 SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA + redis5.0 进行开发

开一个 web 服务用于测试

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

全程应用默认配置,redis 本机,端口 6379,无明码

II. 缓存注解介绍

1. @Cacheable

这个注解用于润饰办法 or 类,当咱们拜访它润饰的办法时,优先从缓存中获取,若缓存中存在,则间接获取缓存的值;缓存不存在时,执行办法,并将后果写入缓存

这个注解,有两个比拟外围的设置

    /**
     * 与 cacheNames 成果等价
     */
    @AliasFor("cacheNames")
    String[] value() default {};

    
    @AliasFor("value")
    String[] cacheNames() default {};

    /**
     * 缓存 key
     */
    String key() default "";

cacheNames 能够了解为缓存 key 的前缀,能够为组件缓存的 key 变量;当 key 不设置时,应用办法参数来初始化,留神 key 为 SpEL 表达式,因而如果要写字符串时,用单引号括起来

一个简略的应用姿态

/**
 * 首先从缓存中查,查到之后,间接返回缓存数据;否则执行办法,并将后果缓存
 * <p>
 * redisKey: cacheNames + key 组合而成 --> 反对 SpEL
 * redisValue: 返回后果
 *
 * @param name
 * @return
 */
@Cacheable(cacheNames = "say", key = "'p_'+ #name")
public String sayHello(String name) {return "hello+" + name + "-->" + UUID.randomUUID().toString();}

如咱们传参为 yihuihui, 那么缓存 key 为 say::p_yihuihui

除了下面三个配置值之外,查看 @Cacheable 注解源码的童鞋能够看到还有 condition 设置,这个示意当它设置的条件达成时,才写入缓存

/**
 * 满足 condition 条件的才写入缓存
 *
 * @param age
 * @return
 */
@Cacheable(cacheNames = "condition", key = "#age", condition = "#age % 2 == 0")
public String setByCondition(int age) {return "condition:" + age + "-->" + UUID.randomUUID().toString();}

下面这个 case 中,age 为偶数的时候,才走缓存;否则不写缓存

接下来是 unless 参数,从名字上能够看出它示意不满足条件时才写入缓存

/**
 * unless, 不满足条件才写入缓存
 *
 * @param age
 * @return
 */
@Cacheable(cacheNames = "unless", key = "#age", unless = "#age % 2 == 0")
public String setUnless(int age) {return "unless:" + age + "-->" + UUID.randomUUID().toString();}

2. @CachePut

不论缓存有没有,都将办法的返回后果写入缓存;实用于缓存更新

/**
 * 不论缓存有没有,都写入缓存
 *
 * @param age
 * @return
 */
@CachePut(cacheNames = "t4", key = "#age")
public String cachePut(int age) {return "t4:" + age + "-->" + UUID.randomUUID().toString();}

3. @CacheEvict

这个就是咱们了解的删除缓存

/**
 * 生效缓存
 *
 * @param name
 * @return
 */
@CacheEvict(cacheNames = "say", key = "'p_'+ #name")
public String evict(String name) {return "evict+" + name + "-->" + UUID.randomUUID().toString();}

4. @Caching

在理论的工作中,常常会遇到一个数据变动,更新多个缓存的场景,对于这个场景,能够通过 @Caching 来实现

/**
 * caching 实现组合,增加缓存,并生效其余的缓存
 *
 * @param age
 * @return
 */
@Caching(cacheable = @Cacheable(cacheNames = "caching", key = "#age"), evict = @CacheEvict(cacheNames = "t4", key = "#age"))
public String caching(int age) {return "caching:" + age + "-->" + UUID.randomUUID().toString();}

下面这个就是组合操作

  • caching::age缓存取数据,不存在时执行办法并写入缓存;
  • 生效缓存 t4::age

5. 异样时,缓存会怎么?

下面的几个 case,都是失常的场景,当办法抛出异样时,这个缓存体现会怎么?

/**
 * 用于测试异样时,是否会写入缓存
 *
 * @param age
 * @return
 */
@Cacheable(cacheNames = "exception", key = "#age")
@Cacheable(cacheNames = "say", key = "'p_yihuihui'")
public int exception(int age) {return 10 / age;}

依据实测后果,当 age==0 时,下面两个缓存都不会胜利

6. 测试用例

接下来验证下缓存注解与下面形容的是否统一

@RestController
public class IndexRest {
    @Autowired
    private BasicDemo helloService;

    @GetMapping(path = {"","/"})
    public String hello(String name) {return helloService.sayHello(name);
    }
}

下面这个次要是验证 @Cacheable 注解,若缓存不命中,每次返回的后果应该都不一样,然而理论拜访时,会发现返回的都是雷同的

curl http://localhost:8080/?name=yihuihui

生效缓存

@GetMapping(path = "evict")
public String evict(String name) {return helloService.evict(String.valueOf(name));
}

生效缓存,须要和下面的 case 配合起来应用

curl http://localhost:8080/evict?name=yihuihui
curl http://localhost:8080/?name=yihuihui

剩下其余的相干测试类就比拟好了解了,一并贴出对应的代码

@GetMapping(path = "condition")
public String t1(int age) {return helloService.setByCondition(age);
}

@GetMapping(path = "unless")
public String t2(int age) {return helloService.setUnless(age);
}

@GetMapping(path = "exception")
public String exception(int age) {
    try {return String.valueOf(helloService.exception(age));
    } catch (Exception e) {return e.getMessage();
    }
}

@GetMapping(path = "cachePut")
public String cachePut(int age) {return helloService.cachePut(age);
}

7. 小结

最初治理小结一下 Spring 提供的几个缓存注解

  • @Cacheable: 缓存存在,则从缓存取;否则执行办法,并将返回后果写入缓存
  • @CacheEvit: 生效缓存
  • @CachePut: 更新缓存
  • @Caching: 都注解组合

下面虽说能够满足常见的缓存应用场景,然而有一个十分重要的点没有阐明,缓存生效工夫应该怎么设置???

如何给每个缓存设置不同的缓存生效工夫,咱么下篇博文见,我是一灰灰,欢送关注长草的公众号 一灰灰 blog

III. 不能错过的源码和相干知识点

0. 我的项目

  • 工程:https://github.com/liuyueyi/spring-boot-demo
  • 源码:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/125-cache-ano

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因集体能力无限,不免有疏漏和谬误之处,如发现 bug 或者有更好的倡议,欢送批评指正,不吝感谢

上面一灰灰的集体博客,记录所有学习和工作中的博文,欢送大家前去逛逛

  • 一灰灰 Blog 集体博客 https://blog.hhui.top
  • 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top

退出移动版