spring-context提供了Cache集成抽象组件方式, 如: CacheManager接口、 Cache接口, @Cacheable 、 @EnableCaching 等。
spring-context-support则提供了多种具体缓存实现。
- @Cacheable 每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
- @CachePut 也可以声明一个方法支持缓存功能。在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。(使用@CachePut时可以指定的属性跟@Cacheable是一样的)
- @CacheEvict 是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。
- value 表示清除操作是发生在哪些Cache上的(对应Cache的名称);
- key 表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;
- condition 表示清除操作发生的条件。
- allEntries 是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。 清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。
- beforeInvocation 可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
- @Caching 可以在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
key自定义策略是指:
- 通过Spring的EL表达式来指定key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时可以直接使用“#参数名”或“#p参数index” 或“#参数名.属性”或“#p参数index.属性”。
@Cacheable(value="users", key="#id") public User find(Integer id) { return null; } @Cacheable(value="users", key="#p0") public User find(Integer id) { return null; } @Cacheable(value="users", key="#user.id") public User find(User user) { return null; } @Cacheable(value="users", key="#p0.id") public User find(User user) { return null; }
- 除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象可以获取到以下信息。
属性名称
描述
示例
methodName
当前方法名
#root.methodName
method
当前方法
#root.method.name
target
当前被调用的对象
#root.target
targetClass
当前被调用的对象的class
#root.targetClass
args
当前方法参数组成的数组
#root.args[0]
caches
当前被调用的方法使用的Cache
#root.caches[0].name
当要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。
@Cacheable(value={"users", "xxx"}, key="caches[1].name") public User find(User user) { return null; }
本文以CaffeineCache接入为例。
- 增加jar包引用
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency>
- 在启动类上声明@EnableCaching,并装载CaffeineCacheManager
CacheName需要提前确认并设置,会在AbstractCacheResolver.resolveCaches从CacheManager中获取import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCacheManager; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; @Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); Caffeine caffeine = Caffeine.newBuilder() // cache的初始容量值 .initialCapacity(100) // maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight不可以同时使用, .maximumSize(200).expireAfterWrite(5, TimeUnit.SECONDS); // 使用refreshAfterWrite必须要设置cacheLoader,但是不能用,因为每个cacheLoader要有差异 //.refreshAfterWrite(2, TimeUnit.SECONDS); // cacheManager.setCacheLoader(cacheLoader()); // cacheManager.setAllowNullValues(false); cacheManager.setCaffeine(caffeine); cacheManager.setCacheNames(Arrays.asList("activityCache", "propCache", "eventCache")); return cacheManager; } /** * 必须要指定这个Bean,refreshAfterWrite这个配置属性才生效 */ private CacheLoader<Object, Object> cacheLoader() { CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() { @Override public Object load(Object key) throws Exception { return null; } // 重写这个方法将oldValue值返回回去,进而刷新缓存 @Override public Object reload(Object key, Object oldValue) throws Exception { return oldValue; } }; return cacheLoader; }
- 使用时: 指定Cache名称、自定义Key值、 前置&后置条件
@Cacheable(value = "activityCache", key = "#actId" , condition ="#actId != '' ", unless = "#result == null") public Activity findById(String actId) { return mongoTemplate.findById(actId, Activity.class); }
- expireAfterWrite: 指定项在一定时间内没有创建/覆盖时,会移除该key,下次取的时候会重新loading;
- refreshAfterWrite:必须指定一个CacheLoader!创建缓存或者最近一次更新缓存后经过固定时间间隔,从指定的CacheLoader中reload刷新缓存。一般不适用注解的方式!
-
@Cacheable 不能配置 condition = "#result != null" ,因为这个注解在进入方法前去检测condition,而这时还没有result,肯定为null;造成一直不能缓存的情况。-----测试正确
-
想要达到参数为空和返回值为空不缓存(后者会出现缓存穿透的现象)当设置cacheManager.setAllowNullValues(false)时<默认true>,targetMethod返回null直接报错:
所以只能使用unless :