真正的缓存之王,Google Guava 只是弟弟(二)-阿里云开发者社区

3. SpringBoot 中默认Cache-Caffine Cache

SpringBoot 1.x版本中的默认本地cache是Guava Cache。在2.x(Spring Boot 2.0(spring 5) )版本中已经用Caffine Cache取代了Guava Cache。毕竟有了更优的缓存淘汰策略。

下面我们来说在SpringBoot2.x版本中如何使用cache。

1. 引入依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.6.2</version> </dependency>

2. 添加注解开启缓存支持

添加@EnableCaching注解:

@SpringBootApplication @EnableCaching public class SingleDatabaseApplication { public static void main(String[] args) { SpringApplication.run(SingleDatabaseApplication.class, args); } }

3. 配置文件的方式注入相关参数

properties文件

spring.cache.cache-names=cache1 spring.cache.caffeine.spec=initialCapacity=50,maximumSize=500,expireAfterWrite=10s

或Yaml文件

spring: cache: type: caffeine cache-names: - userCache caffeine: spec: maximumSize=1024,refreshAfterWrite=60s

如果使用refreshAfterWrite配置,必须指定一个CacheLoader.不用该配置则无需这个bean,如上所述,该CacheLoader将关联被该缓存管理器管理的所有缓存,所以必须定义为CacheLoader<Object, Object>,自动配置将忽略所有泛型类型。

import com.github.benmanes.caffeine.cache.CacheLoader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; / * @author: rickiyang * @date: 2019/6/15 * @description: / @Configuration public class CacheConfig { / * 相当于在构建LoadingCache对象的时候 build()方法中指定过期之后的加载策略方法 * 必须要指定这个Bean,refreshAfterWrite=60s属性才生效 * @return / @Bean public CacheLoader<String, Object> cacheLoader() { CacheLoader<String, Object> cacheLoader = new CacheLoader<String, Object>() { @Override public Object load(String key) throws Exception { return null; } // 重写这个方法将oldValue值返回回去,进而刷新缓存 @Override public Object reload(String key, Object oldValue) throws Exception { return oldValue; } }; return cacheLoader; } }

Caffeine常用配置说明:

initialCapacity=[integer]: 初始的缓存空间大小 maximumSize=[long]: 缓存的最大条数 maximumWeight=[long]: 缓存的最大权重 expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期 expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期 refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存 weakKeys: 打开key的弱引用 weakValues:打开value的弱引用 softValues:打开value的软引用 recordStats:开发统计功能 注意: expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。 maximumSize和maximumWeight不可以同时使用 weakValues和softValues不可以同时使用

需要说明的是,使用配置文件的方式来进行缓存项配置,一般情况能满足使用需求,但是灵活性不是很高,如果我们有很多缓存项的情况下写起来会导致配置文件很长。所以一般情况下你也可以选择使用bean的方式来初始化Cache实例。

下面的演示使用bean的方式来注入:

package com.rickiyang.learn.cache; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.commons.compress.utils.Lists; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; / * @author: rickiyang * @date: 2019/6/15 * @description: / @Configuration public class CacheConfig { / * 创建基于Caffeine的Cache Manager * 初始化一些key存入 * @return / @Bean @Primary public CacheManager caffeineCacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); ArrayList<CaffeineCache> caches = Lists.newArrayList(); List<CacheBean> list = setCacheBean(); for(CacheBean cacheBean : list){ caches.add(new CaffeineCache(cacheBean.getKey(), Caffeine.newBuilder().recordStats() .expireAfterWrite(cacheBean.getTtl(), TimeUnit.SECONDS) .maximumSize(cacheBean.getMaximumSize()) .build())); } cacheManager.setCaches(caches); return cacheManager; } /* * 初始化一些缓存的 key * @return / private List<CacheBean> setCacheBean(){ List<CacheBean> list = Lists.newArrayList(); CacheBean userCache = new CacheBean(); userCache.setKey("userCache"); userCache.setTtl(60); userCache.setMaximumSize(10000); CacheBean deptCache = new CacheBean(); deptCache.setKey("userCache"); deptCache.setTtl(60); deptCache.setMaximumSize(10000); list.add(userCache); list.add(deptCache); return list; } class CacheBean { private String key; private long ttl; private long maximumSize; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public long getTtl() { return ttl; } public void setTtl(long ttl) { this.ttl = ttl; } public long getMaximumSize() { return maximumSize; } public void setMaximumSize(long maximumSize) { this.maximumSize = maximumSize; } } }

创建了一个SimpleCacheManager作为Cache的管理对象,然后初始化了两个Cache对象,分别存储user,dept类型的缓存。当然构建Cache的参数设置我写的比较简单,你在使用的时候酌情根据需要配置参数。

4. 使用注解来对 cache 增删改查

我们可以使用spring提供的 @Cacheable@CachePut@CacheEvict等注解来方便的使用caffeine缓存。

如果使用了多个cahce,比如redis、caffeine等,必须指定某一个CacheManage为@primary,在@Cacheable注解中没指定 cacheManager 则使用标记为primary的那个。

cache方面的注解主要有以下5个:

  • @Cacheable 触发缓存入口(这里一般放在创建和获取的方法上,@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存)
  • @CacheEvict 触发缓存的eviction(用于删除的方法上)
  • @CachePut 更新缓存且不影响方法执行(用于修改的方法上,该注解下的方法始终会被执行)
  • @Caching 将多个缓存组合在一个方法上(该注解可以允许一个方法同时设置多个注解)
  • @CacheConfig 在类级别设置一些缓存相关的共同配置(与其它缓存配合使用)

说一下@Cacheable@CachePut的区别:

@Cacheable:它的注解的方法是否被执行取决于Cacheable中的条件,方法很多时候都可能不被执行。

@CachePut:这个注解不会影响方法的执行,也就是说无论它配置的条件是什么,方法都会被执行,更多的时候是被用到修改上。

简要说一下Cacheable类中各个方法的使用:

public @interface Cacheable { / * 要使用的cache的名字 / @AliasFor("cacheNames") String[] value() default {}; / * 同value(),决定要使用那个/些缓存 / @AliasFor("value") String[] cacheNames() default {}; / * 使用SpEL表达式来设定缓存的key,如果不设置默认方法上所有参数都会作为key的一部分 / String key() default ""; / * 用来生成key,与key()不可以共用 / String keyGenerator() default ""; / * 设定要使用的cacheManager,必须先设置好cacheManager的bean,这是使用该bean的名字 / String cacheManager() default ""; / * 使用cacheResolver来设定使用的缓存,用法同cacheManager,但是与cacheManager不可以同时使用 / String cacheResolver() default ""; / * 使用SpEL表达式设定出发缓存的条件,在方法执行前生效 / String condition() default ""; / * 使用SpEL设置出发缓存的条件,这里是方法执行完生效,所以条件中可以有方法执行后的value / String unless() default ""; /* * 用于同步的,在缓存失效(过期不存在等各种原因)的时候,如果多个线程同时访问被标注的方法 * 则只允许一个线程通过去执行方法 / boolean sync() default false; }

基于注解的使用方法:

package com.rickiyang.learn.cache; import com.rickiyang.learn.entity.User; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; / * @author: rickiyang * @date: 2019/6/15 * @description: 本地cache / @Service public class UserCacheService { / * 查找 * 先查缓存,如果查不到,会查数据库并存入缓存 * @param id / @Cacheable(value = "userCache", key = "#id", sync = true) public void getUser(long id){ //查找数据库 } / * 更新/保存 * @param user / @CachePut(value = "userCache", key = "#user.id") public void saveUser(User user){ //todo 保存数据库 } / * 删除 * @param user / @CacheEvict(value = "userCache",key = "#user.id") public void delUser(User user){ //todo 保存数据库 } }

如果你不想使用注解的方式去操作缓存,也可以直接使用SimpleCacheManager获取缓存的key进而进行操作。

注意到上面的key使用了spEL 表达式。Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

名称

位置

描述

示例

methodName

root对象

当前被调用的方法名

#root.methodname

method

root对象

当前被调用的方法

#root.method.name

target

root对象

当前被调用的目标对象实例

#root.target

targetClass

root对象

当前被调用的目标对象的类

#root.targetClass

args

root对象

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

#root.args[0]

caches

root对象

当前方法调用使用的缓存列表

#root.caches[0].name

Argument Name

执行上下文

当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数

#artsian.id

result

执行上下文

方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false)

#result

注意:

1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如

@Cacheable(key = "targetClass + methodName +#p0")

2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。如:

@Cacheable(value="userCache", key="#id") @Cacheable(value="userCache", key="#p0")

SpEL提供了多种运算符

类型

运算符

关系

<,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne

算术

+,- ,* ,/,%,^

逻辑

&&,||,!,and,or,not,between,instanceof

条件

?: (ternary),?: (elvis)

正则表达式

matches

其他类型

?.,?[…],![…],^[…],$[…]


原网址: 访问
创建于: 2023-10-07 14:44:51
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论