小白教你完全理解|小白教你完全理解 spring 缓存 @Cacheable、@CachePut、@CacheEvict、@Caching
@[TOC]
概述
我们在开发的过程中可能会遇到一些请求比较频繁。但是请求的结果有==基本是不会变化的==(或者是不会变化的)。
而我们每次都要经过运算或者是去数据库中查找结果。这就回导致我们这个接口的效率十分的低下。
那对于这种接口我们就可以请求的时候先看下缓存中又没有保存上一次的结果,如果有就直接返回。
没有就如运算或者去数据库中找。然后存入缓存中就可以了。这样后面的请求就可以直接从缓存中得到结果了。效率自然就上去了。
文章图片
示例图 而 spring 的缓存注解就是为我们提供了这些方法。
@Cacheable、@CachePut、@CacheEvict、@Caching。
下面我们来看下如何使用吧。
==ps:本文所有的演示均以redis作为缓存==
@Cacheable 和 @CachePut
==@Cacheable== 表明spring在调用方法之前会先去缓存中查找又没有之前这个方法的返回值。如果有就直接返回缓存,没有则调用方法,然后将返回值放入缓存之中。
==@CachePut== Spring会将该方法的结果直接放入缓存之中。
两者的区别
【小白教你完全理解|小白教你完全理解 spring 缓存 @Cacheable、@CachePut、@CacheEvict、@Caching】@Cacheable和@CachePut都是填充缓存唯一的不同就是@Cacheable会在调用方法之前检查缓存,而@CachePut则不会。
@Cacheable和@CachePut有都有一些属性。他们共有的属性如下图
属性描述
属性 | 类型 | 描述 |
---|---|---|
value | String[] | 缓存的名称 |
condition | String | SpEL表达式,如果是false则不会将缓存应用到方法调用上 |
key | String | SpEL表达式,缓存的KEY |
unless | String | SpEL表达式,如果是false则不会将方法的返回值存储到缓存上 |
假设我们目前要写一个获取用户个人信息的接口,而这个人的个人信息又不是会经常变化。但是每次进入商城我们都要获取一遍。如果不用缓存那么我们每次都需要去数据库查询一遍。效率就不高。那我们就可以修改成缓存。
// userInfo 为缓存的名称 userId为key,为了区分不同人的信息
@Cacheable(value = "https://www.it610.com/article/userInfo", key = "#userId")
public User getUser(Long userId) {
// 模拟数据库查找
User user=get(userId);
return user;
}
一下就是我们第一次调用这个方法,方法会把返回值存入缓存中。下一次请求就直接会调取缓存中的方法。而不会再去查询。
文章图片
在这里插入图片描述
我们再插入一条
文章图片
在这里插入图片描述
而使用 ==@CachePut== 则会直接调用方法然后将返回值直接更新的缓存中。比如我们的userInfo改变了,需要将新的缓存放入其中 ,那么就可以使用以下代码
@CachePut(value = "https://www.it610.com/article/userInfo", key = "#userId == -1")
public User updateUserInfoCache(Long userId,Boolean) {
// 模拟数据库查找
User user=get(userId);
return user;
}
会直接查询出userInfo信息然后更新到缓存。
自定义缓存key
@Cacheable和@CachePut都带有key属性。key属性可以使用任何SpEL表达式,但是大部分的时候我们会定义与值相关的,用于区分之后可以找到值,比如userInfo的Id
在为编写SpEL表达式的时候spring暴露出一些可以使用的元数据,如下表所示
表达式 | 描述 |
---|---|
#root.args | 传递给缓存的参数,形式为数组 |
#root.caches | 该方法执行时所对应的缓存,形式为数组 |
#root.target | 目标对象 |
#root.targetClass | 目标对象的类,是 #root.target.class 的缩写 |
#root.method | 缓存方法 |
#root.methodName | 缓存方法的名称,是 #root.method.name 的缩写 |
#result | 方法调用的返回值(@Cacheable不可以使用) |
#Argument | Argument可替换成任意方法参数名(如#userId)或者参数索引(如 #a0、#p0) |
//#root.args = [value,value2]
//#root.caches[0].getName = testCache
//#root.target = 当前对象 TestCache
//#root.targetClass = 当前对象类 TestCache
//#root.method = 当前方法 putCache
//#root.methodName = 当前方法名 putCache
//#result = 当前方法的返回值 resultInt
//#value(#Argument替换) = 方法参数value
@CachePut(value = "https://www.it610.com/article/testCache", key="#value")
public Integer putCache(int value,int value2){
int resultInt = value+1;
return resultInt;
}
这里我们一般可以使用方法的参数作为缓存的key。或者是返回值作为key(如 #result.id )。
条件缓存
@Cacheable 和 @CachePut 的==unless==和==condition==属性可以实现条件化缓存。
如果unless属性的SpEL的值返回结果为true。那么方法的返回值不会放到缓存中。
如果condition属性的SpEL的值返回结果为false,那么方法缓存就会被禁用。
表面上看来两者的作用是一样的。但是其实是有一点差别的。
==unless==如果为true,那么会缓存方法的返回值不会放入缓存,但是依然会去缓存中查找又没有匹配的值。如果有则会返回。
==condition==如果为false,那么缓存的方法返回值不会放入缓存,也不会去缓存中查找匹配的值。
下面我们用两个例子来验证以下
我们首先使用下面的方法将一个值放入缓存
@CachePut(value = "https://www.it610.com/article/testCache", key="#root.targetClass.getSimpleName()")
public String putCache(){
return "这是缓存的值";
}
这时我们的缓存中会有一个值。
文章图片
在这里插入图片描述 ==unless==
@Cacheable(value = "https://www.it610.com/article/testCache", key="#root.targetClass.getSimpleName()", unless = "#value=https://www.it610.com/article/=10")
public String putCacheUnless(int value){
System.out.println("执行了方法");
return "保存成功了";
}
测试
System.out.println(testCache.putCacheUnless(10));
返回值如下图,我们可以看出uless为true,此时回去查找缓存中又没有匹配的值,而我们缓存中正好有值,所以直接返回了。
文章图片
在这里插入图片描述
如果我们把缓存删除。然后再次调用,那么结果如下。我们可以看到此时执行了方法。然后把方法的返回值返回了。
文章图片
在这里插入图片描述 ==condition==
@Cacheable(value = "https://www.it610.com/article/testCache", key="#root.targetClass.getSimpleName()", condition = "#value!=10")
public String putCacheCondition(int value){
System.out.println("执行了方法");
return "保存成功了";
}
测试
System.out.println(testCache.putCacheCondition(10));
此时结果如下。此时虽然缓存中有匹配的值,但是还是执行了方法,并返回了方法的返回值。
文章图片
在这里插入图片描述
如果我们把缓存删除了,结果如下,我们可以看到结果没有发生变化可以看出==condition不管缓存中又没有匹配的值都不会去缓存中查找。==
文章图片
在这里插入图片描述 @CacheEvict ==@CacheEvict== 并不会往缓存中放入任何东西。而是会从缓存中删除指定的缓存。一般当缓存不再有效的时候会调取此方法然后删除。
属性描述
属性 | 类型 | 描述 |
---|---|---|
value | String[] | 缓存的名称 |
condition | String | SpEL表达式,如果是false则不会将缓存应用到方法调用上 |
key | String | SpEL表达式,缓存的KEY |
allEntries | boolean | 如果为true的话,无视key,会删除当前缓存下的所有缓存 |
beforeInvocation | boolean | 如果为true的话,会先删除缓存再执行方法,如果为false的话,会先执行方法再删除缓存 |
以上就是我对spring的缓存注解的理解。如果有什么不对的,欢迎大家指出,谢谢。
推荐阅读
- 别墅庭院设计,不同的别墅庭院设计也给人视觉上完全不一样的!
- 社会教你顽强,而不是教你失望
- 驾校小白的“日常”
- 碎片化阅读,四步教你建立自己的知识体系
- 为了女儿,我尝试画画——DIY绘本之《一支铅笔的梦想》(绘画小白的第一篇作品)
- 8种搭配,教你将铅笔裤穿的更有型!
- 小白裙的春天
- 社群培训系统day6复盘—小白学习社群的底层逻辑
- 秋季入冬学会为自己煲汤抵御寒冷吧!教你三大保暖汤
- 适用于小白(VSCode搭建Vue项目,最详细的搭建步骤哦)