Spring Cache的使用
1. 使用入门
1. 添加依赖
在spring boot中使用Spring Caching需要先引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>3.1.5</version>
</dependency>
Spring Caching是构建在Spring Context的基础上的,如果原先没有它的引用的话,需要添加对应的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.13</version>
</dependency>
Spring Context Support里提供了EhCache和Caffeine的CacheManager抽象,如果你打算用这两个缓存实现的话,还有依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>6.0.13</version>
</dependency>
2. 启用缓存
在已经添加了spring-boot-starter-cache
的前提下,只需要使用@EnableCaching
注解,Spring会默认创建一个ConcurrentMapCacheManager
负责缓存,不过它并不支持缓存过期。 除此以外,我们也可以手动创建CacheManager
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users");
}
}
3. 集成EhCache
添加EhCache的依赖
<!-- Ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- Spring Integration for Ehcache -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache-jsr107</artifactId>
</dependency>
在src/main/resources
创建配置文件ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<cache name="card:id"
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="10"
timeToLiveSeconds="30"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
4. @Cachable
这是最常用的注解,如果缓存中存在,返回缓存中的数据,否则调用方法计算,并将结果写入缓存
@Cacheable("card:id")
public String getCardById(Long id) {...}
添加了这个注解,相当于执行伪代码
String data = cacheManager.get(cacheKey)
if(data == null) {
data = getCardById(id)
cacheManager.put(cacheKey, data)
}
return cached;
@Cachable还有一个sync属性,设置为true时会对缓存加载用cacheKey做同步,如果加了注解@Cacheable("card:id", sync=true)
的话,伪代码如下
String data = cacheManager.get(cacheKey)
if(data == null) {
synchronized(cacheKey) {
data = cacheManager.get(cacheKey);
if(data == null) {
data = getCardById(id)
cacheManager.put(cacheKey, data)
}
}
}
return cached;
关于cacheKey的生成逻辑,见后续章节。
5. @CacheEvict
用于从缓存中删除数据,假设我们有一个更新card的接口,调用会清除card:id下的所有缓存。
@CacheEvict(value="card:id", allEntries=true)
public String updateCardById(Long cardId, Customer customer) {...}
我们可以可以清除指定key的数据,key用SpEL来计算
@CacheEvict(value="card:id",, key = "'card_' + #cardId")
public String updateCardById(Long cardId, Customer customer) {...}
6. @CachePut
用于更新缓存数据,想@Cacheable的区别是,它不会检查缓存中是否有数据,始终都调用方法获取数据,并更新缓存。
@CachePut("card:id")
public String getCardById(Long id) {...}
7. @Caching
用在一个方法上有多个缓存操作的时候,比如
@Caching(evict = {
@CacheEvict("addresses"),
@CacheEvict(value="directory", key="#customer.name") })
public String getAddress(Customer customer) {...}
8. @CacheConfig
用于设置service级别的缓存配置,比如CustomerDataService的方法都操作addresses这个缓存,可以这么配置
@CacheConfig(cacheNames={"addresses"})
public class CustomerDataService {
@Cacheable
public String getAddress(Customer customer) {...}
}
9. condition/unless
基于条件的缓存,condition基于入参判断,unless基于返回值判断
@CachePut(value="addresses", condition="#customer.name=='Tom'")
public String getAddress(Customer customer) {...}
@CachePut(value="addresses", unless="#result.length()<64")
public String getAddress(Customer customer) {...}
2. 定制能力
1. Key生成
Spring提供了KeyGenerator
来实现Key的生成,Spring 4.0之后默认采用SimpleKeyGenerator
来生成Key,逻辑如下:
- 方法没有参数的话,默认返回SimpleKey.EMPTY
- 方法就一个参数,返回这个参数值
- 方法有多个参数,将参数封装为
SimpleKey
SimpleKey会计算hashCode来作为缓存读写的Key,如果你不想要这个默认行为,可以通过SpEL自定义Key,比如
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
除此以外,还可以自定义KeyGenerator,将自定义的KeyGenerator定义为bean后,在注解中引用
@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
SpEL中可以使用的变量如下
Name | Description | Example |
---|---|---|
methodName | 方法名 | #root.methodName |
method | 方法 | #root.method.name |
target | 当前方法所属的实例 | #root.target |
targetClass | 当前方法所在的类 | #root.targetClass |
args | 方法的入参 | #root.args[0] |
caches | 对应的缓存 | #root.caches[0].name |
参数名 | 方法入参名,Java编译时带(-parameters )。否则使用#a<#idx> ,其中#idx 是参数的位置(从0开始) | #iban or #a0 |
result | 方法返回值,只在unless 、@CachePut 的key、@CacheEvict 设置beforeInvocation=false 时可用。如果返回值用来包装类(如Optional ),#result 引用的是内部对象 | #result |
2. 自定义CacheManager
Spring提供了EhCache和Caffeine的默认实现,如果使用没有默认实现的Cache,可以通过自定义CacheManager来实现
@Bean
CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheSpecification(...);
cacheManager.setAsyncCacheMode(true);
return cacheManager;
}
3. 实现原理
1. Cache抽象
Spring定义了一个Cache接口,用来实现缓存的写入、读取和清理,通过CacheManager、CacheResolver创建Cache对象,集成Spring Boot Starter Cache的时候其实就是创建CacheManager。通过将KeyGenerator生成缓存key,传递给Cache,来设置或读取缓存。
2. 注解实现
通过AOP代理了标注@Cacheable、@CacheEvict、@CachePut等注解的方法,程序的入口在ProxyCachingConfiguration中,他会创建Advisor和Interceptor,实现对Bean对象的AOP。BeanFactoryCacheOpertionSourceAdvisor内部使用CacheOperationSource来过滤切点类,如果我们是基于Annotation来使用缓存的话,实现类是AnnotationCacheOperationSource,它负责失败方法上的@Cacheable等注解。实际的缓存逻辑由CacheInterceptor实现,核心代码在CacheAspectSupport类内,尤其是execute方法。
CacheAspectSupport的execute方法的核心逻辑就是生成key、读取缓存、实际调用方法、写入缓存。
A. 参考文档
- https://docs.spring.io/spring-framework/reference/integration/cache/annotations.html
原文地址:https://blog.csdn.net/randavy/article/details/145168426
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!