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使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java如何将list按照指定数量分成小list

    java如何将list按照指定数量分成小list

    本文介绍了四种不同的方法对集合进行分区操作,包括手动编写代码、使用Guava库、Apache Commons Collection库以及Java 8的流操作,每种方法都有其特点和适用场景,需要注意的是,部分方法返回的是原集合的视图,而部分则返回的是新的集合
    2024-11-11
  • Spring项目中Ordered接口的应用之全局过滤器(GlobalFilter)的顺序控制

    Spring项目中Ordered接口的应用之全局过滤器(GlobalFilter)的顺序控制

    在Spring框架,尤其是Spring Cloud Gateway或Spring WebFlux项目中,Ordered接口扮演着重要的角色,特别是在实现全局过滤器(GlobalFilter)时,用于控制过滤器执行的优先级,下面将介绍如何在Spring项目中使用Ordered接口来管理Global Filter的执行顺序,需要的朋友可以参考下
    2024-06-06
  • MybatisPlus使用代码生成器遇到的小问题(推荐)

    MybatisPlus使用代码生成器遇到的小问题(推荐)

    这篇文章主要介绍了MybatisPlus使用代码生成器遇到的小问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • java注解的全面分析

    java注解的全面分析

    这篇文章主要介绍了java注解的全面分析的相关资料,Java提供的一种原程序中的元素关联任何信息和任何元数据的途径和方法,需要的朋友可以参考下
    2017-08-08
  • springboot application无法使用$获取pom变量的问题及解决

    springboot application无法使用$获取pom变量的问题及解决

    这篇文章主要介绍了springboot application无法使用$获取pom变量的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Springboot使用redis实现接口Api限流的示例代码

    Springboot使用redis实现接口Api限流的示例代码

    本文主要介绍了Springboot使用redis实现接口Api限流的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Java中Supplier和Consumer接口的使用超详细教程

    Java中Supplier和Consumer接口的使用超详细教程

    文章阐述了Java8中Supplier与Consumer函数式接口的设计理念及应用价值,强调其通过封装数据供给与消费逻辑,提升代码灵活性与可维护性,对比传统匿名内部类展现简洁优势,并在策略、观察者等设计模式实现高效应用,为函数式编程提供标准化工具,感兴趣的朋友跟随小编一起看看吧
    2025-08-08
  • Java编写实现九宫格应用

    Java编写实现九宫格应用

    这篇文章主要为大家详细介绍了Java编写实现九宫格应用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Java获取用户IP属地模拟抖音详解

    Java获取用户IP属地模拟抖音详解

    细心的小伙伴可能会发现,抖音新上线了 IP 属地的功能,小伙伴在发表动态、发表评论以及聊天的时候,都会显示自己的 IP 属地信息,本篇文章我们来模拟实现这一功能
    2022-07-07
  • 如何在Spring data中使用r2dbc详解

    如何在Spring data中使用r2dbc详解

    这篇文章主要给大家介绍了关于如何在Spring data中使用r2dbc的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11

最新评论