自学内容网 自学内容网

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,逻辑如下:

  1. 方法没有参数的话,默认返回SimpleKey.EMPTY
  2. 方法就一个参数,返回这个参数值
  3. 方法有多个参数,将参数封装为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中可以使用的变量如下

NameDescriptionExample
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. 参考文档

  1. https://docs.spring.io/spring-framework/reference/integration/cache/annotations.html

原文地址:https://blog.csdn.net/randavy/article/details/145168426

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!