目标:学会使用SpringCache查询缓存注解并理解它的原理
背景
Redis访问工具
项目使用Redis存储缓存数据,如何通过Java去访问Redis?
常用的有Jedis和Lettuce两个访问redis的客户端库,其中Lettuce的性能和并发性要好一些,Spring Boot 默认使用的是 Lettuce 作为 Redis 的客户端。
集成Spring data redis框架,在项目中可以通过RedisTemplate访问Redis,RedisTemplate提供了方便访问redis的模板方法。
Spring Cache最终也是通过Lettuce 去访问redis 。
使用Spring Cache的方法很简单,只需要在方法上添加注解即可实现将方法返回数据存入缓存,以及清理缓存等注解的使用。
Spring Cache基本介绍
Spring Cache是Spring提供的一个缓存框架,基于AOP原理,实现了基于注解的缓存功能,只需要简单地加一个注解就能实现缓存功能,对业务代码的侵入性很小。
基于SpringBoot使用Spring Cache非常简单,首先加入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.10</version>
</dependency>
| 简单认识它的常用注解: | |
|---|---|
| @EnableCaching: | 开启缓存注解功能 |
| @Cacheable: | 查询数据时缓存,将方法的返回值进行缓存 |
| @CacheEvict: | 用于删除缓存,将一条或多条数据从缓存中删除 |
| @CachePut: | 用于更新缓存,将方法的返回值放到缓存中 |
| @Caching: | 组合多个缓存注解 |
| @CacheConfig: | 统一配置@Cacheable中的value值 |
查询数据时缓存@Cacheable:
下边使用Cacheable注解实现查询服务信息时对服务信息进行缓存,它的执行流程是:第一次查询服务信息缓存中,没有该服务的信息此时去查询数据库,查询数据库拿到服务信息并进行缓存,第二次再去查询该服务信息,发现缓存中有该服务的信息, 则直接查询缓存不再去数据库查询。
流程如下:
1.首先在工程启动类中添加@EnableCaching注解,它表示开启Spring cache缓存组件。
2.在IServeService接口中定义如下接口:
/**
* 查询区域服务信息并进行缓存
* @param id 对应serve表的主键
* @return 区域服务信息
*/
Serve queryServeByIdCache(Long id);
3.在接口实现类中定义如下方法:
@Override
public Serve queryServeByIdCache(Long id) {
return getById(id);
}
此时该方法还是查询数据库。
下边在方法中添加Cacheable注解:
// @Cacheable(value = "JZ_CACHE:SERVE_RECORD",key = "#id")
@Cacheable(value = RedisConstants.CacheName.SERVE,key = "#id")
public Serve queryServeByIdCache(Long id) {
return getById(id);
}
Cacheable注解配置的两项参数说明:
value:缓存的名称,缓存名称作为缓存key的前缀。
key: 缓存key,支持SpEL表达式,上述代码表示取参数id的值作为key
最终缓存key为:缓存名称+“::”+key,例如:上述代码id为123,最终的key为:测试 JZ_CACHE:SERVE_RECORD::123
SpEL(Spring Expression Language)是一种在 Spring 框架中用于处理字符串表达式的强大工具,它可以实现获取对象的属性,调用对象的方法操作。
keyGenerator:指定一个自定义的键生成器(实现 org.springframework.cache.interceptor.KeyGenerator 接口的类),用于生成缓存的键。与 key 属性互斥,二者只能选其一。
4.测试
测试@CachePut
CachePut注解实现的是将方法的返回值放到缓存中。
在服务上架后会将区域服务的信息写入缓存,服务下架会从缓存删除,下边我们实现服务上架将服务写入缓存。
找到服务上架的方法,在方法上添加@CachePut注解:
@Override
@Transactional
@CachePut(value = RedisConstants.CacheName.SERVE, key = "#id", cacheManager = RedisConstants.CacheManager.ONE_DAY)
public Serve onSale(Long id){
....
上边代码同样指定了缓存名称、缓存key及缓存管理器(缓存过期时间为一天)。
编写单元测试方法测试服务上架方法。
找一个草稿或下架状态的服务执行上架操作,也可以在serve表中找一个测试数据更改状态为0。
//服务上架测试
@Test
public void test_onSale(){
//从serve表找一条下架的服务(sale_status '售卖状态,0:草稿,1下架,2上架',)
Serve serve = serveService.onSale(1715263395009191938L);
Assert.notNull(serve,"服务为空");
}
测试@CacheEvict
下边测试服务下架删除缓存。
找到服务下架的方法,添加@CacheEvict注解
@Override
@Transactional
@CacheEvict(value = RedisConstants.CacheName.SERVE, key = "#id")
public Serve offSale(Long id){
....
工作原理
Spring Cache是基于AOP原理,对添加注解 @Cacheable 的类生成代理对象,在方法执行前查看是否有缓存对应的数据,如果有直接返回数据,如果没有调用源方法获取数据返回,并缓存起来,下边跟踪Spring Cache的切面类CacheAspectSupport.java中的private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)方法。 如下图: