Spring 从 3.1 版本开始定义缓存抽象来统一不同的缓存技术;在应用层面与后端存储之间,提供了一层抽象,这层抽象目的在于封装各种可插拔的后端存储( ehcache, redis, guava),最小化因为缓存给现有业务代码带来的侵入。
一、Spring Cache简介
Spring Cache 是 Spring 框架提供的一个缓存抽象,实现了基于注解的缓存功能,它能够轻松地集成到 Spring 应用程序中,为方法调用的结果提供缓存支持,从而提高应用程序的性能和响应速度。
Spring Cache 提供了一种声明式的缓存机制,可以在方法上使用注解来指定需要进行缓存的方法,而无需编写繁琐的缓存代码。
Spring Cache 基本介绍:
- 声明式缓存支持:Spring Cache 提供了一种声明式的缓存机制,通过在方法上使用注解来声明需要进行缓存的方法。比如:@Cacheable、@CachePut、@CacheEvict等注解。
- 灵活的缓存策略:Spring Cache 提供了多种灵活的缓存策略,可以根据具体需求选择合适的缓存方案。比如:基于内存缓存、基于 Redis、Ehcache 等第三方缓存框架的集成。
- 支持缓存管理器:Spring Cache 支持多种缓存管理器,可以根据需要选择适合的缓存管理器。比如:ConcurrentMapCacheManager、EhCacheCacheManager、RedisCacheManager 等。
- 适用于各种应用场景:Spring Cache 适用于各种应用场景,可以用于缓存方法的返回值、类级别的缓存、缓存的条件判断等。
Spring Cache核心抽象主要体现在两个接口上:
org.springframework.cache.cache接口
:cache代表缓存组件规范定义(缓存本身),包含缓存的各种操作集合。比如:增加、删除、读取等。org.springframework.cache.cachemanager接口
:CacheManager 是 Spring 各种缓存的抽象接口。抽象的意义在于屏蔽实现细节的差异和提供扩展性,这一层cache的抽象解耦了缓存的使用和缓存的后端存储,这样后续可以方便的更换后端存储。
Spring提供了 Cache接口下各种 XxxCache实现。比如:RedisCache, EhCacheCache, ConcurrentMapCache, JCacheCache等。
Spring支持的常用 CacheManager 如下:
Spring Cache 默认使用 ConcurrentMapCacheManager 作为缓存管理器。
通过使用 Spring Cache,可以轻松地为应用程序中的方法调用提供缓存支持,从而提高应用程序的性能和响应速度,减少对数据库或其他资源的频繁访问。
每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已经被调用过。
- 如果有就直接从缓存中获取方法调用后的结果。
- 如果没有就调用方法并缓 存结果后返回给用户。下次调用直接从缓存中获取。
二、Spring Cache使用
Spring Cache依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
在spring boot项目中,无需添加 Spring Cache 的依赖,因为 Spring Boot 已经自动集成了 Spring Cache。
1、接入Redis使用Spring Cache
添加 Redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
项目配置文件(application.yml)中添加 Redis配置信息:
spring:
## Redis 配置信息
redis:
host: 127.0.0.1
port: 6379
password: xxxxxx
database: 1
1.1 Redis配置类
Redis默认使用 JdkSerializationRedisSerializer序列化器。
- RedisTemplate 中,k和v默认的序列化方案是 JdkSerializationRedisSerializer 。
- RedisTemplate<String, Object> 中,k和v默认的序列化方案是 JdkSerializationRedisSerializer 。
- StringRedisTemplate 中,k和v默认的序列化方案是 StringRedisSerializer 。默认情况下,前面不会有前缀。
@Configuration
public class RedisConfiguration {
@Primary
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.java());
redisTemplate.setHashValueSerializer(RedisSerializer.java());
redisTemplate.setConnectionFactory(factory);
// 调用 afterPropertiesSet()方法执行初始化操作
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
1.2 Spring Cache配置类
@EnableCaching注解:开启缓存功能,一般放在启动类或者配置类上。
CacheManager可以不用注入,Spring框架会自动注入的。如果我们手动注入,会使用我们自己注入的(适合自定义相关配置信息)。
下面使用 RedisCacheManager作为缓存缓存管理。
@EnableCaching
@Configuration
public class CacheConfiguration {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager cacheManager = RedisCacheManager.create(connectionFactory);
return cacheManager;
}
}
2、核心注解使用
Spring Cache提供了下面几个核心注解完成缓存功能的实现。
- @EnableCaching:开启缓存功能
- @Cacheable:定义缓存,用于触发缓存
- @CachePut:定义更新缓存,触发缓存更新
- @CacheEvict:定义清除缓存,触发缓存清除
- @Caching:组合定义多种缓存功能
- @CacheConfig:定义公共设置,位于class之上
2.1 @Cacheable注解
@Cacheable注解:定义缓存数据或者获取缓存数据操作。
@Cacheable 注解可以应用于类或方法上。当应用于类上时,表示该类的所有方法的返回值都可以被缓存;当应用于方法上时,表示特定方法的返回值可以被缓存。
该注解标注的方法每次被调用前都会触发缓存校验,校验指定参数的缓存是否已存在(已发生过相同参数的调用),若存在,直接返回缓存结果,否则执行方法内容,最后将方法执行结果保存到缓存中。
2.1.1 主要属性
主要属性如下:
- value:必填,指定缓存数据存放在哪块命名空间(缓存名)。用于指定要操作的缓存名称或缓存管理器的名称。可以是一个字符串,也可以是一个字符串数组,用于指定多个缓存名称。
- cacheNames:与 value 差不多,二选一即可。
- key:可选,指定缓存条目的键,用于唯一标识缓存中的条目。SpEL 表达式可以用于动态地生成键值。如果不指定 key,Spring Cache 将会使用默认的键生成策略来生成键值。
- condition:条件符合则缓存。指定一个 SpEL 表达式,用于判断是否执行缓存操作。只有当表达式的值为 true 时,才会执行缓存操作。默认为空,表示始终执行缓存操作。
- unless:条件符合则不缓存。指定一个 SpEL 表达式,用于判断是否不执行缓存操作。只有当表达式的值为 false 时,才会执行缓存操作。默认为空,表示始终执行缓存操作。
- keyGenerator:指定一个自定义的键生成器,用于生成缓存条目的键值。默认为空,表示使用默认的键生成策略。key/keyGenerator二选一使用。
- cacheManager:指定一个自定义的缓存管理器,用于指定缓存的实现。默认为空,表示使用默认的缓存管理器。
- cacheResolver:指定一个自定义的缓存解析器,用于解析缓存的实现。默认为空,表示使用默认的缓存解析器。
- sync:指定是否启用同步模式。如果设置为 true,那么缓存操作将在调用方法的同一线程中执行,否则将在后台线程中异步执行。默认为 false。
2.1.2 使用
@Cacheable(value = "userCache_", key = "#id", unless = "#result==null")
@Override
public UserDTO getById(Long id) {
if (id == null || id <= 0L) {
return null;
}
UserDO userDO = userMapper.selectById(id);
return do2DTO(userDO);
}
unless参数里的 #result为方法的返回值。
所以,判断方法返回值为 null 时,则不缓存。
2.2 @CachePut注解
@CachePut注解:更新缓存数据操作。
@CachePut与 @Cacheable 不同,@CachePut 不会检查缓存中是否已经存在相同键的条目,而是直接将方法的返回值更新到缓存中。
该注解用于更新缓存,无论结果是否已经缓存,都会在方法执行结束插入缓存,相当于更新缓存。一般用于更新方法之上。
2.2.1 主要属性
主要属性如下:
- value:必填,指定缓存数据存放在哪块命名空间(缓存名)。用于指定要操作的缓存名称或缓存管理器的名称。可以是一个字符串,也可以是一个字符串数组,用于指定多个缓存名称。
- cacheNames:与 value 差不多,二选一即可。
- key:可选,指定缓存条目的键,用于唯一标识缓存中的条目。SpEL 表达式可以用于动态地生成键值。如果不指定 key,Spring Cache 将会使用默认的键生成策略来生成键值。
- condition:条件符合则缓存。指定一个 SpEL 表达式,用于判断是否执行缓存操作。只有当表达式的值为 true 时,才会执行缓存操作。默认为空,表示始终执行缓存操作。
- unless:条件符合则不缓存。指定一个 SpEL 表达式,用于判断是否不执行缓存操作。只有当表达式的值为 false 时,才会执行缓存操作。默认为空,表示始终执行缓存操作。
- keyGenerator:指定一个自定义的键生成器,用于生成缓存条目的键值。默认为空,表示使用默认的键生成策略。key/keyGenerator二选一使用
- cacheManager:指定一个自定义的缓存管理器,用于指定缓存的实现。默认为空,表示使用默认的缓存管理器。
- cacheResolver:指定一个自定义的缓存解析器,用于解析缓存的实现。默认为空,表示使用默认的缓存解析器。
2.2.2 使用
@CachePut(cacheNames = "userCache_", key = "#updateRequest.userId", unless = "#result == null")
@Override
public UserDTO update2(UserUpdateRequest updateRequest) {
Long id = updateRequest.getUserId();
if (id == null || id <= 0) {
return null;
}
// 修改用户信息
UserDO updateDO = new UserDO();
BeanUtils.copyProperties(updateRequest, updateDO);
updateDO.setUpdateTime(LocalDateTime.now());
userMapper.updateById(updateDO);
// 演示效果
UserDO userDO = userMapper.selectById(id);
return do2DTO(userDO);
}
注意:
这里设置的 key一定要和执行缓存保存的方法设置 key一致,否则无法准确更新。
使用方法参数作为 key的示例:
@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
// 推荐使用
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
除了上述使用方法参数作为 key之外,Spring还为我们提供了一个 root对象可以用来生成 key。通过该 root对象我们可以获取到以下信息。
2.3 @CacheEvict注解
@CacheEvict注解:删除缓存数据操作。
2.3.1 主要属性
主要属性如下:
- value:必填,指定缓存数据存放在哪块命名空间(缓存名)。用于指定要操作的缓存名称或缓存管理器的名称。可以是一个字符串,也可以是一个字符串数组,用于指定多个缓存名称。
- cacheNames:与 value 差不多,二选一即可。
- key:可选,指定缓存条目的键,用于唯一标识缓存中的条目。SpEL 表达式可以用于动态地生成键值。如果不指定 key,Spring Cache 将会使用默认的键生成策略来生成键值。
- condition:条件符合则缓存。指定一个 SpEL 表达式,用于判断是否执行缓存操作。只有当表达式的值为 true 时,才会执行缓存操作。默认为空,表示始终执行缓存操作。
- eyGenerator:指定一个自定义的键生成器,用于生成缓存条目的键值。默认为空,表示使用默认的键生成策略。key/keyGenerator二选一使用。
- cacheManager:指定一个自定义的缓存管理器,用于指定缓存的实现。默认为空,表示使用默认的缓存管理器。
- cacheResolver:指定一个自定义的缓存解析器,用于解析缓存的实现。默认为空,表示使用默认的缓存解析器。
- allEntries:是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存。
- beforeInvocation:是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存。
2.3.2 使用
@CacheEvict(value = "userCache_", key = "#id", condition = "#result.success == true")
//@CacheEvict(value = "userCache_", allEntries = true)
@Override
public BaseOperateResult deleteById(Long id) {
BaseOperateResult operateResult = new BaseOperateResult();
if (id == null || id <= 0L) {
operateResult.setSuccess(false);
operateResult.setErrorCode(DemoErrorCodeEnum.ID_IS_NOT_NULL.errorCode());
return operateResult;
}
UserDTO oldDTO = getById(id);
if (oldDTO == null) {
operateResult.setSuccess(Boolean.FALSE);
operateResult.setErrorCode(DemoErrorCodeEnum.RECORD_NOT_FOUND.errorCode());
return operateResult;
}
// 不允许删除管理员账号
if ("admin".equals(oldDTO.getUsername())) {
operateResult.setSuccess(Boolean.FALSE);
operateResult.setErrorCode(DemoErrorCodeEnum.NOT_DELETE_ADMIN_USER.errorCode());
return operateResult;
}
// 用户逻辑删除
UserDO updateDO = new UserDO();
updateDO.setUserId(id);
updateDO.setDelFlag(CommonConstants.DELETE_FLAG_DELETED);
userMapper.updateById(updateDO);
operateResult.setId(id);
operateResult.setSuccess(true);
return operateResult;
}
注意:
- 这里设置的 key一定要和执行缓存保存的方法设置 key一致,否则无法准确删除。
- condition参数里的 #result为方法的返回值。所以,判断方法返回值的 success = true 时,则执行删除缓存操作。
- 如果配置了 allEntries属性,则会删除 value为命名空间下的所有缓存条目的键,此时配不配 key属性都不会影响 allEntries属性操作。
3、全局配置Cache参数信息(了解)
参考文章:spring-boot+redis实现缓存功能:https://www.jianshu.com/p/be2c09cd27d8
项目配置文件(application.yml)中可以添加 cache配置信息。
注意:
- 如果使用 Spring框架自动注入 CacheManager的方式,下面配置会生效。
- 如果使用我们手动注入的自定义了 CacheManager方式,下面配置不会生效,而是使用我们自定义的相关配置。
spring:
## Redis 配置信息
redis:
host: 127.0.0.1
port: 6379
password: xxxxxx
database: 1
## Spring cache配置信息
cache:
# 缓存的类型,redis
type: redis
redis:
# 缓存数据key是否使用前缀,默认是true
use-key-prefix: true
#缓存数据key的前缀,在上面的配置为true时有效,
key-prefix: 'demo:'
# redis中缓存超时的时间,默认60000ms
time-to-live: 120000
参考文章:
- SpEL表达式详解:https://blog.csdn.net/weixin_43888891/article/details/127520555
– 求知若饥,虚心若愚。