Spring缓存注解@Cacheable @CacheEvit @CachePut使用介绍

 更新时间:2021年07月05日 10:51:29   作者:一灰灰  
Spring在3.1版本,就提供了一条基于注解的缓存策略,实际使用起来还是很丝滑的,本文将针对几个常用的注解进行简单的介绍说明,有需要的小伙伴可以尝试一下

Spring在3.1版本,就提供了一条基于注解的缓存策略,实际使用起来还是很丝滑的,本文将针对几个常用的注解进行简单的介绍说明,有需要的小伙伴可以尝试一下

本文主要知识点:

  • @Cacheable: 缓存存在,则使用缓存;不存在,则执行方法,并将结果塞入缓存
  • @CacheEvit: 失效缓存
  • @CachePut: 更新缓存

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

到此这篇关于Spring缓存注解@Cacheable @CacheEvit @CachePut使用介绍的文章就介绍到这了,更多相关Spring @Cacheable @CacheEvit @CachePut内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java concurrency之互斥锁_动力节点Java学院整理

    Java concurrency之互斥锁_动力节点Java学院整理

    本文通过示例代码给大家介绍了Java concurrency之互斥锁的相关知识,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-06-06
  • Java中ThreadLocal避免内存泄漏的方法详解

    Java中ThreadLocal避免内存泄漏的方法详解

    ThreadLocal是Java中的一个线程本地存储机制,它允许每个线程拥有一个独立的本地存储空间,用于存储该线程的变量,本文主要介绍了ThreadLocal如何避免内存泄漏,需要的朋友可以参考下
    2023-05-05
  • IDEA中的.iml文件和.idea文件夹使用方式

    IDEA中的.iml文件和.idea文件夹使用方式

    这篇文章主要介绍了IDEA中的.iml文件和.idea文件夹使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Spring Boot项目如何同时支持HTTP和HTTPS协议的实现

    Spring Boot项目如何同时支持HTTP和HTTPS协议的实现

    这篇文章主要介绍了Spring Boot项目如何同时支持HTTP和HTTPS协议的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Spring Boot RestTemplate提交表单数据的三种方法

    Spring Boot RestTemplate提交表单数据的三种方法

    本篇文章主要介绍了Spring Boot RestTemplate提交表单数据的三种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • Java数组的特性_动力节点Java学院整理

    Java数组的特性_动力节点Java学院整理

    数组是基本上所有语言都会有的一种数据类型,它表示一组相同类型的数据的集合,具有固定的长度,并且在内存中占据连续的空间。在C,C++等语言中,数组的定义简洁清晰,而在Java中确有一些会让人迷惑的特性。本文就尝试分析这些特性
    2017-04-04
  • 如何自定义springboot-starter日志组件供各个服务使用(系统日志优化)

    如何自定义springboot-starter日志组件供各个服务使用(系统日志优化)

    文章介绍了如何将各个微服务的接口调用日志逻辑优化为一个可共享的Spring Boot Starter,通过自定义注解和自动装配机制实现,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-01-01
  • java中计算集合的交差并集示例代码

    java中计算集合的交差并集示例代码

    今天突然想Java如何计算集合的交差并集,主要是看Python语言的时候想起来的。下面这篇文章主要给大家介绍了关于java中计算集合的交差并集的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-08-08
  • idea如何关闭页面显示的浏览器图标

    idea如何关闭页面显示的浏览器图标

    这篇文章主要介绍了idea如何关闭页面显示的浏览器图标问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Java客户端利用Jedis操作redis缓存示例代码

    Java客户端利用Jedis操作redis缓存示例代码

    Jedis是Redis官方推荐的用于访问Java客户端,下面这篇文章主要给大家介绍了关于Java客户端利用Jedis操作redis缓存的相关资料,文中给出了详细的示例代码,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-07-07

最新评论