缓存控制(@CacheConfig @EnableCaching @Caching @Cacheable @CachePut @CacheEvict)

Exisi 2022-06-28 08:17:55
Categories: Tags:
  • 3.1开始,Spring 引入了对 Cache 的支持。其使用方法和原理都类似于 Spring 对事务管理的支持。

 

  • Spring Cache 是作用在方法上的,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时直接从缓存中获取结果进行返回。

所以在使用 Spring Cache 的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果

 

  • Spring为我们提供了几个注解来支持 Spring Cache。其核心主要是 @Cacheable @CacheEvict。使用 @Cacheable 标记的方法在执行后 Spring Cache 将缓存其返回结果,而使用 @CacheEvict 标记的方法会在方法执行前或者执行后移除 Spring Cache 中的某些元素。下面我们将来详细介绍一下 Spring 基于注解对 Cache 的支持所提供的几个注解

 

注解

说明

@Cacheable

产生缓存

指示一个方法(或类上的所有方法)触发缓存查询或更新操作,即优先使用缓存结果,有则跳过执行方法

 

当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。

 

对于支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果

 

参数

描述

cacheNames

value

指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。

key

缓存数据时的key的值,默认是使用方法所有入参的值,可以使用SpEL表达式表示key的值。

keyGenerator

key 的生成器。 key keyGenerator 二选一使用

cacheManager

指定缓存管理器(例如ConcurrentHashMapRedis)

condition

Spring表达式语言(SpEL)表达式,用于使缓存回收操作有条件。

unless

否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)

sync

是否使用同步模式。

在多线程环境下,某些操作可能使用相同参数同步调用。默认情况下,缓存不锁定任何资源,可能导致多次计算,而违反了缓存的目的。对于这些特定的情况,属性 sync 可以指示底层将缓存锁住,使只有一个线程可以进入计算,而其他线程堵塞,直到返回结果更新到缓存中。

cacheResolver

要使用的自定义实现了CacheResolver接口类的bean名。

 

@CachePut

更新缓存

指示一个方法(或类上的所有方法)触发缓存更新操作。与@Cacheable注释不同,该注解不会在跳过已缓存的方法,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。如果condition()unless()表达式匹配,它总是会调用方法并将其结果存储在关联的缓存中

 

参数

描述

cacheNames

value

指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。

key

缓存数据时的key的值,默认是使用方法所有入参的值,可以使用SpEL表达式表示key的值。

keyGenerator

key 的生成器。 key keyGenerator 二选一使用

cacheManager

指定缓存管理器(例如ConcurrentHashMapRedis)

condition

Spring表达式语言(SpEL)表达式,用于使缓存回收操作有条件。当为true时表示进行缓存处理;当表达式为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次

unless

否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然也可以对获取到结果进行判断。(通过 #result 获取方法结果)

cacheResolver

要使用的自定义实现了CacheResolver接口类的bean名。

 

@CacheEvict

删除缓存

指示一个方法(或类上的所有方法)执行都触发缓存清除操作。该注释可以用作元注释,以创建带有属性覆盖的自定义组合注释

 

参数

描述

cacheNames

value

指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。

key

缓存数据时的key的值,默认是使用方法所有入参的值,可以使用SpEL表达式表示key的值。

keyGenerator

key 的生成器。 key keyGenerator 二选一使用

cacheManager

指定缓存管理器(例如ConcurrentHashMapRedis)

condition

Spring表达式语言(SpEL)表达式,用于使缓存回收操作有条件。

cacheResolver

要使用的自定义实现了CacheResolver接口类的bean名。

allEntries

是否删除缓存中的所有条目。

beforeInvocation

是否应该在调用方法之前发生清除,该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素

 

@CacheConfig

提供一种在类级别共享公共缓存相关设置的机制。当此注解出现在给定的类上时,它会为该类中定义的任何缓存操作提供一组默认设置。

 

使用 @CacheConfig 注释,我们可以在类级别将一些缓存配置精简到一个位置,这样就不必多次声明

 

参数

描述

cacheNames

value

指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。

keyGenerator

key 的生成器。 指定生成缓存键值的策略。这个属性可以接受一个实现了KeyGenerator接口的类作为参数,也可以使用SpEL表达式来动态生成键值。

cacheManager

可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。

cacheResolver

要使用的自定义实现了CacheResolver接口类的bean名。

 

@Caching

多个缓存注释(不同或相同类型)的组注释。即对@Cacheable @CachePut @CacheEvict的复合(combine)操作。此注解可用作元注解,以创建具有属性覆盖的自定义组合注解

 

参数

描述

cacheable

@Cacheable的注解列表,默认{}

put

@CachePut的注解列表,默认{}

evict

@CacheEvict的注解列表,默认{}

 

 @EnableCaching

开启缓存

在类上设置当前缓存的一些公共设置,比如使用这个注解作用在类上,就可以为此类的所有方法的缓存注解提供默认值、缓存的默认名称、缓存的KeyGenerator

 

参数

描述

proxyTargetClass

指示是否要创建基于子类 (CGLIB) 的代理而不是基于标准 Java 接口的代理。默认false

mode

指示应如何应用缓存建议

order

当在特定的连接点应用多个建议时,请指示缓存顾问执行顺序。

 

示例

@Service

//开启缓存

@EnableCaching

//共享的缓存设置

@CacheConfig(cacheNames={"addresses"})

public class RedisService {

    @Autowired

    UserInfoMapper userInfoMapper;

 

    //将方法运行结果进行缓存,当方法被再次调用时,直接返回缓存中的结果。

    @Cacheable(value = "test", key = "#id")

    public String getUserById(Integer id) {

        UserInfo userInfo=userInfoMapper.getUserById(id);

        return userInfo.toString();

    }

 

    //修改了数据库的数据,同时更新缓存。先调用目标方法,然后缓存方法结果。

    @CachePut(value = "test",key="#result.id")  //只能是result.id

    public UserInfo updateUser(UserInfo userInfo) {

        userInfoMapper.updateUser(userInfo);

        return userInfo;

    }

 

    //删除数据之后,清除缓存

    @CacheEvict(value = "test", key = "#id")

    public String deleteUser(Integer id) {

        userInfoMapper.deleteUserById(id);

        return "已删除";

    }

 

    //同时组合多个注解

    @Caching(

evict = {

@CacheEvict("address"),

@CacheEvict(value = "test1", key = "#id")

        }

put = {

@CachePut(value = "test2",key="#result.id"),

@CachePut(value = "test3",key="#result.name")

},

cacheable = {

@Cacheable(value = "test4", key = "#id"),

@Cacheable(value = "test5", key = "#name"),

}

    )

    public String getAddress(UserInfo userInfo)

        

    }

 

}

  • key 的生成策略有两种,一种是默认策略,一种是自定义策略。

key生成策略

说明

默认策略

通过 KeyGenerator 生成

自定义策略

通过SpringEL表达式来指定

 

  •  默认的 key 生成策略是通过 KeyGenerator 生成的,其默认策略如下:

 

  1. 如果方法没有参数,则使用 0 作为 key

 

  1. 如果只有一个参数的话则使用该参数作为 key

 

  1. 如果参数多余一个的话则使用所有参数的 hashCode 作为 key

 

  • 如果需要自定义默认生成策略,可以实现 KeyGenerator,然后指定 Spring Cache 使用的 KeyGenerator 为自定义实现的 KeyGenerator

 

<cache:annotation-driven key-generator="userKeyGenerator"/>

<bean id="userKeyGenerator" class="com.xxx.cache.UserKeyGenerator"/>

 

  • 或者基于XML配置的 <cache:advice> 来指定的。

<cache:advice id="cacheAdvice" cache-manager="cacheManager" key-generator="userKeyGenerator">

</cache:advice>

 

  • 自定义策略是指可以通过 Spring EL 表达式来指定 key。这里的 EL 表达式可以使用方法参数及它们对应的属性。使用方法参数时可以直接使用 #参数名” 或者 #p参数index

 

 

 

使用 SpEL 表达式条件

  • Spring Cache 提供了一些供我们使用的SpEL上下文数据:

名称

位置

描述

示例

methodName

root对象

当前被调用的方法名

root.methodName

method

root对象

当前被调用的方法

root.method.name

target

root对象

当前被调用的目标对象

root.target

targetClass

root对象

当前被调用的目标对象类

root.targetClass

args

root对象

当前被调用的方法的参数列表

root.args[0]

caches

root对象

当前方法调用使用的缓存列表(如@Cacheable(value={cache1, cache2})),则有两个cache

root.caches[0].name

argument name

执行上下文

当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数

user.id

result

执行上下文

方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache evict’的beforeInvocation=false

result

示例

@Service

//开启缓存

@EnableCaching

public class RedisService {

@CacheEvict(value = "user",

key = "#user.id",

condition = "#root.target.canCache()

and

#root.caches[0].get(#user.id).get().username

ne

#user.username", beforeInvocation = true

)

public void conditionUpdate(User user) {

}

 

}

 

来自 <https://www.cnblogs.com/qlqwjy/p/8559119.html>