1、Cacheable注解

作用:

对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。

源代码:

在这里插入图片描述

  • @Target、@Retention、@Inherited是三个元注解
  • @Documented是关于生成Javadoc文档的
  • @AliasFor是Spring框架的一个注解,用于声明注解属性的别名

相关说明:

  • @Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。
  • 支持缓存,即Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法
  • 缓存是以键值对的形式,值就是方法的返回结果,键有两种生成策略,默认策略和自定义策略

2、@Cacheable注解属性

@Cacheable注解有九个属性:

2.1 value/cacheName属性

指定将方法的返回结果放在哪个缓存中。源码中这个属性是String数组类型,即可以指定多个缓存位置

2.2 key属性

缓存以键值对的形式,key用来指定键,String类型。默认使用的是方法调用传过来的参数作为 key:

  • 如果请求没有参数:key=new SimpleKey()
  • 如果请求有一个参数:key=参数的值
  • 如果请求有多个参数:key=newSimpleKey(params)

手动定义key值的时候,可以使用SpEL表达式(Spring Expression Language,即Spring表达语言):

选项位置描述示例
methodNameroot object当前被调用的方法名#root.method.name
methodroot object当前被调用的方法#root.methodName
targetroot object当前被调用的目标对象#root.target
targetClassroot object当前被调用的目标对象类#root.targetClass
argsroot object当前被调用的方法的参数列表#root.args[0]
cachesroot object当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”,“cache2”})),则有两个cache#root.caches[0].name
argument nameevaluation context方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引;#id、#p0、#a0
resultevaluation context方法执行后的返回值(仅当方法执行之后的判断有效,如’unless’、'cache put’的表达式 'cacheevict’的表达式beforeInvocation=false)#result

其中,#参数名和其他字符串使用+号拼接出一个key的做法很常见

@Cacheable(value = "redis_myCache",key = "'mykey:' + #root.method.name + #p0")

2.3 keyGenerator属性

key的生成器。keyGenerator和key两个属性二选一使用。keyGenerator可以通过自定义配置类方式,将 keyGenerator 注册到 IOC 容器来使用。

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){

            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+ Arrays.asList(params).toString();
            }
        };
    }

    /**
     * 支持 lambda 表达式编写
     */
    /*@Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return ( target,  method, params)-> method.getName()+ Arrays.asList(params).toString();
    }*/
}

2.4 cacheManager属性

指定缓存管理器,项目中使用不同的缓存技术,需要选择不同的cacheManager:

CacheManger描述
SimpleCacheManager使用简单的Collection来存储缓存,主要用于测试
ConcurrentMapCacheManager使用ConcurrentMap作为缓存技术(默认)
NoOpCacheManager测试用
EhCacheCacheManager使用EhCache作为缓存技术,以前在hibernate的时候经常用
GuavaCacheManager使用google guava的GuavaCache作为缓存技术
HazelcastCacheManager使用Hazelcast作为缓存技术
JCacheCacheManager使用JCache标准的实现作为缓存技术,如Apache Commons JCS
RedisCacheManager使用Redis作为缓存技术

2.5 cacheResolver属性

即缓存解析器,该属性与cacheManager 是互斥的,只能指定一个。

2.6 condition属性

条件,指定符合什么条件的时候,才进行缓存

@Cacheable(condition = "#id>0")

//传入的 id 参数值>0才进行缓存
@Cacheable(condition = "#a0>1 and #root.methodName eq 'getUserInfo'")

//传入的第一个参数的值>1 且 方法名为 getUserInfo 的时候才进行缓存


2.7 unless属性

unless除非,条件满足时,不缓存,和condition是条件满足时缓存,二者相反

@Cacheable(unless = "#result == null")

//当方法返回值为 null 时,结果不缓存
@Cacheable(unless = "#a0 == null")

//当第一个参数值为 null 时,就不缓存

2.8 sync属性

指定是否使用异步模式,默认false,即同步模式。异步模式sync = true时,unless属性不可用

3、@Cacheable注解不生效

在方法或者类上加了@Cacheable注解,但是数据并未被缓存到Redis。先检查配置问题:

  • 配置文件中启用缓存
spring.cache.type=redis

在这里插入图片描述

  • 启动类加@EnableCaching注解

在这里插入图片描述

再考虑代码层的问题:

  • 缓存的对象必须实现Serializable接口

在这里插入图片描述

  • @Cacheable注解的实现是AOP,AOP又得依赖代理对象,一个方法A调同一个类里的另一个有缓存注解的方法B,此时不走缓存 (内部调用不用代理对象 => 不用代理对象调用加了注解的方法 => AOP增强后的方法调用不到 =>那就一普通方法 =>注解自然不生效)。简单说就是注解的背后基本都是AOP一堆代码,而想AOP方法,得用代理对象去调用

在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐