SpringBootCache使用
约 2685 字大约 9 分钟
2025-01-26
copy:https://www.cnblogs.com/ejiyuan/p/11014765.html
1. 简言
Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Caffeine、Ehcache等),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。
SpringBoot在annotation的层面实现了数据缓存的功能,基于Spring的AOP技术。所有的缓存配置只是在annotation层面配置,像声明式事务一样。
Spring定义了CacheManager和Cache接口统一不同的缓存技术。其中CacheManager是Spring提供的各种缓存技术的抽象接口。而Cache接口包含缓存的各种操作。
Cache接口下Spring提供了各种xxxCache的实现,如RedisCache,EhCacheCache ,ConcurrentMapCache等
2. 缓存技术类型与CacheManger
针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现。
CacheManger | 描述 |
---|---|
SimpleCacheManager | 使用简单的Collection来存储缓存,主要用于测试 |
ConcurrentMapCacheManager | 使用ConcurrentMap作为缓存技术(默认),需要显式的删除缓存,无过期机制 |
NoOpCacheManager | 仅测试用途,不会实际存储缓存 |
EhCacheCacheManager | 使用EhCache作为缓存技术,以前在hibernate的时候经常用 |
GuavaCacheManager | 使用google guava的GuavaCache作为缓存技术(1.5版本已不建议使用) |
CaffeineCacheManager | 是使用Java8对Guava缓存的重写,spring5(springboot2)开始用Caffeine取代guava |
HazelcastCacheManager | 使用Hazelcast作为缓存技术 |
JCacheCacheManager | 使用JCache标准的实现作为缓存技术,如Apache Commons JCS |
RedisCacheManager | 使用Redis作为缓存技术 |
常规的SpringBoot已经为我们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用ConcurrentMapCacheManager
。SpringBoot的application.properties配置文件,使用spring.cache前缀的属性进行配置。
3. 缓存依赖
spring-boot-starter-cache 为基础依赖,其他依赖根据使用不同的缓存技术选择加入,默认情况下使用 ConcurrentMapCache不需要引用任何依赖
<!-- 基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 使用 ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- 使用 caffeine https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 使用 redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4.yml配置
spring:
# 本地缓存
cache:
# 缓存的技术类型,可选 redis,caffeine,simple,cache2k,couchbase,generic,hazelcast,infinispan,jcache,none
type: caffeine
# 应用程序启动创建缓存的名称,必须将所有注释为@Cacheable缓存name(或value)罗列在这里,否则:Cannot find cache named 'xxx' for Builder[xx]
cache-names: demo_cache
caffeine:
# maximumSize: 缓存最大条目数(基于LRU淘汰策略)。
# expireAfterAccess: 条目在最后一次访问后过期时间(单位:秒)
# expireAfterWrite: 条目在写入后过期时间(如 expireAfterWrite=5m)
# recordStats: 开启缓存统计(通过 cache.getNativeCache().stats() 获取)
spec: maximumSize=1000,expireAfterAccess=600s,recordStats
5.缓存注解
5.0 @Cacheable和@CachePut的区别
- @Cacheable:它的注解的方法是否被执行取决于 Cacheable 中的条件,方法很多时候都可能不被执行
- @CachePut:这个注解不会影响方法的执行,也就是说无论它配置的条件是什么,方法都会被执行,更多的时候是被用到修改上
5.1 @EnableCaching
在启动类注解@EnableCaching开启缓存
@SpringBootApplication
@EnableCaching //开启缓存
public class DemoApplication{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
5.2 @Cacheable
配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问
参数:
- value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。 由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了。
- key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式, 比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值。
- keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器, 我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的。
- cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用。
- cacheResolver:用于指定使用那个缓存解析器,非必需。需通过
org.springframework.cache.interceptor.CacheResolver
接口来实现自己的缓存解析器,并用该参数指定。 - condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存, 比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存。
- unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机, 该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
public class BotRelationServiceImpl implements BotRelationService {
@Override
@Cacheable(value = {"newJob"},key = "#p0")
public List<NewJob> findAllLimit(int num) {
return botRelationRepository.findAllLimit(num);
}
}
5.3 @CachePut
主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable不同的是,它每次都会触发真实方法的调用。 简单来说就是用户更新缓存数据。但需要注意的是该注解的value和key必须与要更新的缓存相同,也就是与@Cacheable相同。
@CachePut(value = "newJob", key = "#p0") //按条件更新缓存
public NewJob updata(NewJob job) {
NewJob newJob = newJobDao.findAllById(job.getId());
newJob.updata(job);
return job;
}
5.4 @CacheEvict
配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。 除了同@Cacheable一样的参数之外,它还有下面两个参数:
- allEntries:非必需,默认为false。当为true时,会移除所有数据。如:@CachEvict(value=”testcache”,allEntries=true)
- beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。 如:@CachEvict(value=”testcache”,beforeInvocation=true)
@Cacheable(value = "emp",key = "#p0.id")
public NewJob save(NewJob job) {
newJobDao.save(job);
return job;
}
//清除一条缓存,key为要清空的数据
@CacheEvict(value="emp",key="#id")
public void delect(int id) {
newJobDao.deleteAllById(id);
}
//方法调用后清空所有缓存
@CacheEvict(value="accountCache",allEntries=true)
public void delectAll() {
newJobDao.deleteAll();
}
//方法调用前清空所有缓存
@CacheEvict(value="accountCache",beforeInvocation=true)
public void delectAll() {
newJobDao.deleteAll();
}
5.5 @CacheConfig
统一配置本类的缓存注解的属性,在类上面统一定义缓存的名字,方法上面就不用标注了,当标记在一个类上时则表示该类所有的方法都是支持缓存的
@CacheConfig(cacheNames = {"myCache"})
public class BotRelationServiceImpl implements BotRelationService {
@Override
@Cacheable(key = "targetClass + methodName +#p0")//此处没写value
public List<BotRelation> findAllLimit(int num) {
return botRelationRepository.findAllLimit(num);
}
}
6. 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="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],[1],$[…] |
7. Caffeine Cache 使用
Caffeine是使用Java8对Guava缓存的重写版本,在Spring Boot 2.0中将取代,基于LRU算法实现,支持多种缓存过期策略。
7.1 Caffeine参数说明
- initialCapacity=[integer]: 初始的缓存空间大小
- maximumSize=[long]: 缓存的最大条数
- maximumWeight=[long]: 缓存的最大权重
- expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期
- expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期
- refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存 refreshAfterWrite requires a LoadingCache
- weakKeys: 打开key的弱引用
- weakValues:打开value的弱引用
- softValues:打开value的软引用
- recordStats:开发统计功能
注意:
- refreshAfterWrite必须实现LoadingCache,跟expire的区别是,指定时间过后,expire是remove该key,下次访问是同步去获取返回新值,而refresh则是指定时间后,不会remove该key,下次访问会触发刷新,新值没有回来时返回旧值
- expireAfterWrite和expireAfterAccess同事存在时,以expireAfterWrite为准。
- maximumSize和maximumWeight不可以同时使用
- weakValues和softValues不可以同时使用
7.2 依赖
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<!--<version>3.2.0</version>-->
</dependency>
7.3 Caffeine配置
通过配置文件来设置Caffeine
spring:
cache:
cache-names: outLimit,notOutLimit
caffeine:
spec: initialCapacity=50, maximumSize=500, expireAfterWrite=5s, refreshAfterWrite=7s
type: caffeine
(2)通过bean装配
@Bean()
@Primary
public CacheManager cacheManagerWithCaffeine() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
Caffeine caffeine = Caffeine.newBuilder()
.initialCapacity(100) //cache的初始容量值
.maximumSize(1000) //maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight不可以同时使用,
.maximumWeight(100) //控制最大权重
.expireAfter(customExpireAfter) //自定义过期
.refreshAfterWrite(5, TimeUnit.SECONDS); //使用refreshAfterWrite必须要设置cacheLoader
cacheManager.setCaffeine(caffeine);
cacheManager.setCacheLoader(cacheLoader); //缓存加载方案
cacheManager.setCacheNames(getNames()); //缓存名称列表
cacheManager.setAllowNullValues(false);
return cacheManager;
}
(3)配置文件结合Bean装配
@Value("${caffeine.spec}")
private String caffeineSpec;
@Bean(name = "caffeineSpec")
public CacheManager cacheManagerWithCaffeineFromSpec(){
CaffeineSpec spec = CaffeineSpec.parse(caffeineSpec);
Caffeine caffeine = Caffeine.from(spec); // 或使用 Caffeine caffeine = Caffeine.from(caffeineSpec);
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeine);
cacheManager.setCacheNames(getNames());
return cacheManager;
}
7.4 实现cacheLoader
CacheLoader是cache的一种加载策略,key不存在或者key过期之类的都可以通过CacheLoader来自定义获得/重新获得数据。 使用refreshAfterWrite必须要设置cacheLoader
@Configuration
public class CacheConfig {
@Bean
public CacheLoader<Object, Object> cacheLoader() {
CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() {
@Override
public Object load(Object key) throws Exception {
return null;
}
// 达到配置文件中的refreshAfterWrite所指定的时候回处罚这个事件方法
@Override
public Object reload(Object key, Object oldValue) throws Exception {
return oldValue; //可以在这里处理重新加载策略,本例子,没有处理重新加载,只是返回旧值。
}
};
return cacheLoader;
}
}
… ↩︎