目录
[1、@Cacheable 缓存结果](#1、@Cacheable 缓存结果)
[2、@CachePut 更新缓存](#2、@CachePut 更新缓存)
[3、@CacheEvict 清除缓存](#3、@CacheEvict 清除缓存)
[4、@Caching 组合缓存(不常用)](#4、@Caching 组合缓存(不常用))
[5、@CacheConfig 类级别缓存配置(不常用)](#5、@CacheConfig 类级别缓存配置(不常用))
[6、@CacheResult 设置缓存超时(不常用)](#6、@CacheResult 设置缓存超时(不常用))
[四 、优劣势](#四 、优劣势)
一、概述
Spring Cache 是 Spring Framework 提供的一个用于操作缓存的抽象层,它简化了在 Spring 应用程序中集成和使用缓存的过程
Spring Cache 不直接实现任何缓存技术,而是定义了一套标准接口和注解,使得开发人员可以轻松地切换底层缓存实现,而无需修改业务逻辑代码
是不是有点似懂非懂,那么使用一个🌰来了解
🌰:一般会将用户相关信息存储到缓存中,为了提高用户信息的查询效率,那么就是如下操作:
java
@Service
public class UserService {
@Autowire
private UserMapper userMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
//查询用户
public User getUserById(Long userId) {
String userKey = "userId_" + userId;
Uesr user = (User) redisTemplate.opsForValue().get(userKey);
if(user != null) {
return user;
}
user = userMapper.getUserById(userId);
if(user != null) {
redisTemplate.opsForValue().set(userKey, user);
return user;
}
}
//更新用户
public User updateUser(User user) {
userMapper.updateUser(user);
String userKey = "userId_" + userId;
return (User) redisTemplate.opsForValue().get(userKey);
}
//删除用户
public void deleteUser(Long userId){
userMapper.deleteUserById(userId);
String userKey = "userId_" + userId.getId();
redisTemplate.delete(userKey);
}
}
一般都会是这样写,非常容易理解,但是
- 代码不够优雅,每次读用户信息查询、新增、修改、删除,都需要调用缓存命令的API,产生很多重复代码
- 代码有侵入性 ,缓存操作和业务逻辑之间的代码耦合度高
侵入性主要体现如下两点:
-
开发联调阶段,需要去掉缓存,只能注释或者临时删除缓存操作代码,也容易出错;
-
某些场景下,需要更换缓存组件,每个缓存组件有自己的API,更换成本颇高
那么为了解决上述问题,Spring Cache提供了一种简单的方式来处理缓存,解决了手动管理缓存的复杂性
二、缓存注解
Spring Cache 提供了五个注解:
@Cacheable
:标记在方法上,表示该方法的结果应该被缓存
根据方法的请求参数对其结果进行缓存,下次同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法
-
@CachePut
:更新缓存中的值,通常用于更新缓存而不改变返回值 -
@CacheEvict:根据一定的条件删除缓存
-
@Caching:组合多个缓存注解
-
@CacheConfig:类级别共享缓存相关的公共配置
1、@Cacheable 缓存结果
@Cacheable
注解用于声明一个方法的结果是可缓存的
@Cacheable注解属性介绍
-
value
或cacheNames
: 指定缓存名称,可以是单个或多个 -
key
: 指定缓存键的SpEL表达式。 -
condition
: 指定缓存的条件SpEL表达式。 -
unless
: 指定不缓存的条件SpEL表达式
使用 Spring Cache 的示例代码如下:
java
@Cacheable(value = "users", key = "#userId")
public User getUser(String userId) {
// 从数据库中获取用户信息
return findUserById(userId);
}
在这个示例中, getUser()
方法的返回值会被缓存,缓存的键是 userId
。每次调用这个方法时,都会检查缓存中是否存在对应的用户信息
多个缓存名称和条件:
java
@Cacheable(value = {"users","userInfo"}, //缓存名称
key = "#userId"。 //缓存键,使用SpEL表达式
condition = "#userId.length() > 3", // 缓存条件,只有当userId长度大于3时才缓存
unless = "#result == null" // 除非条件,如果结果为null,则不缓存
)
public User getUser(String userId) {
// 从数据库中获取用户信息
return findUserById(userId);
}
2、@CachePut 更新缓存
@CachePut
注解用于在方法执行后更新缓存,属性与 @Cacheable
相同
java
@CachePut(value = "users", key = "#user.id", condition = "#user.age > 18" )
public User updateUser(User user) {
// 更新用户信息并缓存
return updateUserInDatabase(user);
}
3、@CacheEvict 清除缓存
@CacheEvict
注解用于在方法执行后清除缓存
@CacheEvict
注解属性介绍
-
value
或cacheNames
: 指定缓存名称,可以是单个或多个。 -
allEntries
: 清除所有缓存项。 -
condition
: 指定清除缓存的条件SpEL表达式
java
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
// 从数据库中删除用户信息
deleteUserFromDatabase(userId);
}
4、@Caching 组合缓存(不常用)
@Caching有三个属性:cacheable、put和evict,分别用于指定
@Cacheable
, @CachePut
, @CacheEvict
。对于一个数据的变动,更新多个缓存的场景
@Caching
注解用于组合多个缓存操作
java
@Caching(
cacheable = {@Cacheable(value = "userCache", key = "#userId")},
put = {@CachePut(value = "userCache", key = "#user.id")},
evict = { @CacheEvict(value = "archivedUsers", key = "#userId")}
)
public User updateUser(Long userId, String name) {
return findUserByIdAndName(userId, name);
}
5、@CacheConfig 类级别缓存配置(不常用)
**@CacheConfig
**注解用于在类级别提供缓存相关的共享配置
@CacheConfig注解说明
-
cacheNames
: 指定类中所有缓存操作的默认缓存名称。 -
keyGenerator
: 指定默认的缓存键生成器。 -
condition
: 指定类中所有缓存操作的默认条件
java
@CacheConfig(cacheNames = "userCache", keyGenerator = "customKeyGenerator")
public class UserService {
// 类中的方法可以使用缓存注解
}
6、@CacheResult 设置缓存超时(不常用)
@
CacheResult 是一个非官方的注解,用于替代 @Cacheable
,提供额外的配置选项,如超时时间
@CacheResult注解说明
-
cacheName
: 缓存名称。 -
condition
: 缓存条件。 -
timeout
: 缓存超时时间(毫秒)
java
@CacheResult(cacheName = "users", timeout = 3600000) 设置超时时间为1小时
public User getUserById(String userId) {
// 从数据库中获取用户信息
return findUserById(userId);
}
三、使用方式
1、**@EnableCaching
**开启缓存
在主配置类或启动类上添加 **@EnableCaching
**注解来启用缓存功能
java
@EnableCaching
public class UserApplication{
......
}
2、在方法上添加注解**@Cacheable
**等注解
因此对上面例子🌰使用SpringCache,代码示例如下
java
@Service
public class UserService {
@Cacheable(value = "users", key = "#userId")
public User getUserById(String userId) {
// 从数据库中获取用户信息
return findUserById(userId);
}
@CachePut(value = "users", key = "#user.id", condition = "#user.age > 18" )
public User updateUser(User user) {
// 更新用户信息并缓存
return updateUserInDatabase(user);
}
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
// 从数据库中删除用户信息
deleteUserFromDatabase(userId);
}
}
在这个示例中, @Cacheable
注解用于缓存 getUser
方法的结果,@CachePut
用于更新用户信息并缓存,@CacheEvict
用于删除缓存中的用户信息
通过这些注解,可以轻松管理缓存,而不需要直接使用 RedisTemplate
总之,Spring Cache 通过提供高层次的抽象和简化的 API,解决了手动管理缓存所带来的复杂性,使得开发者能够更专注于业务逻辑的实现
四 、优劣势
Spring Cache相对于手动使用RedisTemplate的一些优势和劣势的问题
优势
- 简化代码 :使用 Spring Cache,您可以通过注解(如
@Cacheable
,@CachePut
,@CacheEvict
)来声明缓存,而不需要手动编写缓存的逻辑。这样可以减少样板代码,提高代码的可读性和可维护性 - 统一的缓存抽象 :Spring Cache 提供了一个统一的 API,可以与多种缓存解决方案(如 Redis, Ehcache, Caffeine 等)集成。这样,可以在不同的缓存实现之间切换,而不需要修改业务逻辑代码
- 自动化处理 :Spring Cache 可以自动处理缓存的创建、更新和失效。例如,当方法被调用时,Spring Cache 会自动检查缓存中是否存在结果,如果存在则直接返回,不再执行方法
- 灵活配置:可以通过配置文件或注解轻松地配置缓存的行为,例如设置缓存的过期时间、最大大小等
- 支持方法级别的缓存:通过注解,可以对特定的方法进行缓存,而不需要在整个类中实现缓存逻辑,这样更加灵活
劣势
- 抽象层次高:隐藏了底层缓存实现的复杂性。这可能导致开发者对缓存的工作原理不够了解,从而在调试和优化时遇到困难
- 缓存一致性问题:当数据更新时,缓存可能不会立即失效,导致数据不一致。在分布式系统中,确保缓存和数据库之间的一致性是一个挑战
关于缓存一致性问题可以看这篇:Redis与MySQL数据一致性问题的策略模式及解决方案_mysql redis 数据一致性 实现方案-CSDN博客
- 复杂的配置:对于复杂的缓存策略(例如,基于时间的过期策略、缓存清理等),可能需要额外的配置和代码,增加了实现的复杂性
- 性能开销:在某些情况下,Spring Cache 的抽象层可能会引入额外的性能开销,尤其是在高并发环境下,可能会对性能产生影响
五、总结
Spring缓存机制通过**@
EnableCaching** 开启,配合 **@Cacheable
, @CachePut
, @CacheEvict
**等注解,为Java应用提供了一种声明式管理缓存的方式。这些注解使得缓存配置变得简洁明了,允许开发者轻松实现数据的自动缓存、更新和清除,从而优化应用性能,减少不必要的计算和数据访问开销