(1). Spring Cache是什么
Spring比较喜欢做的一件事情就是:定义规范(抽象),然后,相同类型的产品对规范进行实现(类似于:桥梁模式),可以理解:Spring Cache是Spring针对Cache定义的一套规范.
比如:你在工作中要用到:Redis/Memcache/Tair/Guava…等等,使用Spring Cache你可以无缝自由切换(组合)这些缓存的实现.
(2). 缺陷
Spring Cache与Redis结合时,过期时间(TTL)是全局的,即所有的KEY都是相同的过期时间(是否有考虑过缓存雪崩问题),若想为某一个key单独配置过期时间,是不支持.
深入看了源码,也没有发现有预留或者暴露出接口方便扩展,当然,你也可以自己写(CacheManager),但是,感觉成本有点大(若要解决:缓存穿透/缓存击穿/缓存雪崩,还是得自己去扩展CacheManager).
个人觉得Spring应该定义一些规范,在Cache的内部,增加上相应生命周期(回调函数).
(3). @Cacheable注解
使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中.
@Cacheable(
// 定义缓存的名称
cacheNames="users",
// 使用key属性自定义key
// #p0 : 通过下标读取参数的第一个元数
// #p0.id : 通过下标读取参数的第一个元数的属性id
// #user.id : 通过参数名称读取属性id
// #root.methodName : 当前方法名
// #root.method.name : 当前方法
// #root.target : 目标对象
// #root.targetClass : 目标Class
// #root.args[0] : 方法参数组
// #root.caches[0].name : 当前被调用的方法使用的Cache(caceNames)
// "#productId + '_' + #imgType.id" : 多个key进行组合
key="#p0" ,
// 通过condition指定符合条件,则缓存,不符合条件的数据,则不缓存.
condition = "#user.id % 2 == 0" ,
// 告诉底层的缓存提供者将缓存的入口锁住,这样就只能有一个线程计算操作的结果值,而其它线程需要等待,这样就避免了 n-1 次数据库访问.
// sync=true,可以避免缓存击穿问题
// Cache.get(Object key, Callable<T> valueLoader)
// 实际上是依赖于java的synchronized关键字
// synchronized <T> T get(Object key, Callable<T> valueLoader)
// sync = false
)
(4). @CachePut注解
@CachePut与@Cacheable不同的是,@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中.
(5). @CacheEvict注解
@CacheEvict是用来标注在需要清除缓存元素
@CacheEvict(
// allEntries为true时,Spring Cache将忽略指定的key,清除所有的元素.
allEntries = false,
// 清除操作默认是在"调用代理类的方法成功后"执行之后触发清除操作.
// 如果被代理后的方法抛出异常而未能成功返回时,则不会触发清除操作.
// 使用beforeInvocation可以改变触发清除操作的时间.
// 当我们指定该属性值为true时,Spring会在调用"代理方法之前"清除缓存中的指定元素.
beforeInvocation = false
)
(6). @Caching注解
@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解. 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict.