Spring Cache的使用示例详解

 更新时间:2025年01月17日 16:32:00   作者:randy.lou  
SpringCache是构建在SpringContext基础上的缓存实现,提供了多种缓存注解,如@Cachable、@CacheEvict、@CachePut等,本文通过实例代码介绍了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
  • 方法就一个参数,返回这个参数值
  • 方法有多个参数,将参数封装为SimpleKeySimpleKey会计算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. 参考文档 https://docs.spring.io/spring-framework/reference/integration/cache/annotations.html

到此这篇关于Spring Cache的使用的文章就介绍到这了,更多相关Spring Cache使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springmvc DispatcherServlet原理及用法解析

    Springmvc DispatcherServlet原理及用法解析

    这篇文章主要介绍了Springmvc DispatcherServlet原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • java中的Reference和引用类型实例精讲

    java中的Reference和引用类型实例精讲

    这篇文章主要为大家介绍了java中的Reference和引用类型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Java生成二维码的两种实现方式(基于Spring Boot)

    Java生成二维码的两种实现方式(基于Spring Boot)

    这篇文章主要给大家介绍了关于Java生成二维码的两种实现方式,文中的代码基于Spring Boot,本文基于JAVA环境,以SpringBoot框架为基础开发,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Lucene源码系列多值编码压缩算法实例详解

    Lucene源码系列多值编码压缩算法实例详解

    这篇文章主要为大家介绍了Lucene源码系列多值编码压缩算法实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • springboot中将日志信息存储在catalina.base中过程解析

    springboot中将日志信息存储在catalina.base中过程解析

    这篇文章主要介绍了springboot中将日志信息存储在catalina.base中过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • 使用Maven创建和管理多模块项目的详细步骤

    使用Maven创建和管理多模块项目的详细步骤

    使用Maven进行多模块项目管理是一种常见的做法,它可以帮助你组织大型项目,使其结构更加清晰,便于维护和构建,以下是使用Maven创建和管理多模块项目的详细步骤,需要的朋友可以参考下
    2024-10-10
  • Spark-Sql入门程序示例详解

    Spark-Sql入门程序示例详解

    Spark SQL 作为 Spark 四大核心组件之一,主要用于处理结构化数据或半结构化数据,它支持在Spark 中使用 SQL 对数据进行查询,本文给大家介绍Spark-Sql入门程序,感兴趣的朋友跟随小编一起看看吧
    2021-12-12
  • SpringBoot访问外部文件及默认路由问题

    SpringBoot访问外部文件及默认路由问题

    这篇文章主要介绍了SpringBoot访问外部文件及默认路由问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 关于SpringBoot静态资源路径管理问题

    关于SpringBoot静态资源路径管理问题

    这篇文章主要介绍了SpringBoot静态资源路径管理,主要包括默认静态资源路径,增加静态资源路径前缀的相关操作,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • java web开发之servlet图形验证码功能的实现

    java web开发之servlet图形验证码功能的实现

    这篇文章主要为大家详细介绍了java web开发之servlet中图形验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11

最新评论