SpringBoot结合Redis实现缓存管理功能
作者:棒棒糖_
一、概述
目标:
- 熟悉Spring Cache基础组件及作用
- 熟悉Spring Cache常用注解及作用
- 掌握SpringBoot结合Redis实现缓存管理
二、 Spring Cache基础组件
keyGenerator
keyGenerator
是用于生成缓存键(Cache Key)的组件。缓存键是用于在缓存中唯一标识缓存数据的值。默认情况下,Spring Cache 使用参数列表作为缓存键。但在某些情况下,如果需要自定义缓存键的生成逻辑,则可以创建自定义的KeyGenerator
实现并配置到 Spring 中,如果没有自定义的话Spring提供默认的keyGenerator
——SimplekeyGenerator
,它会根据方法参数列表生成一个唯一的缓存 key。cacheManager
在 Spring Cache 框架中,
CacheManager
是用于管理缓存实例的组件。它可以创建和管理多个缓存,每个缓存都有一个唯一的名称。当开发者在项目中配置了 Redis(或其他支持的缓存中间件),Spring 会自动使用相应的CacheManager
实现,如RedisCacheManager
。 如果没有配置任何缓存中间件,Spring 默认会采用SimpleCacheManager
作为缓存管理器。SimpleCacheManager
内部使用ConcurrentHashMap
来维护缓存数据,它是一个线程安全的 HashMap 实现。这种情况下,并不需要额外的缓存中间件,Spring 会将缓存数据存储在内存中。cacheResolver 和cacheManager作用一样,使用时二选一
三、 缓存管理注解
@EnableCaching
该注解用于启用 Spring Framework 的缓存支持。通常在配置类上使用,表示开启缓存机制。有一个可选参数 proxyTargetClass,默认值为 false,表示使用 JDK 动态代理实现 AOP,如果设置为 true,则表示使用 CGLIB 代理进行 AOP。
@CacheConfig
@CacheConfig 注解用于配置缓存的公共属性,如缓存名称、缓存管理器等。可以在类级别上使用,表示该类中的所有方法都具有相同的缓存规则。 该注解可选参数:
- cacheNames:指定缓存名称。
- keyGenerator:指定缓存 key 生成策略。
- cacheManager:指定缓存管理器。
@CacheAble
@Cacheable
注解用于标注方法的返回值可以被缓存,通常用于查询操作。如果缓存中存在对应的数据,则直接从缓存中获取数据返回,否则执行方法并将返回值存入缓存中。该注解可选参数:cacheNames:指定缓存名称。
key:指定缓存 key,需要使用 SpEL 表达式进行动态计算,没有指定的话,会使用
keyGenerator
。cacheName
和key
可以组合成最终用于在 Redis 等缓存中存储数据的 key。通常情况下,cacheName
代表缓存的名称,而key
则用于标识缓存中的具体数据。当使用@Cacheable(cacheNames = "myCache", key = "#user.id")
这样的注解时,Spring 将会根据给定的cacheName
和key
生成一个唯一的缓存 key,然后将方法返回的数据缓存到 Redis 中。- 例如,在 Redis 中可能会生成类似于
myCache::123
这样的键来存储缓存数据,其中myCache
是缓存名称,123
则是根据#user.id
表达式计算得出的具体键值。
condition:指定一个 SpEL 表达式,当条件为 true 时才会进行缓存操作。
unless:指定一个 SpEL 表达式,当条件为 false 时才会进行缓存操作。
condition
属性是在方法执行前计算的,因此无法获取到方法返回结果。unless
属性是在方法执行后计算的,因此可以拿到方法返回结果,即SpEL 表达式中可以使用#result获取到方法返回值。
sync:指定缓存是否需要同步更新。默认情况下,Spring Cache 不会对缓存进行同步更新,即在多线程环境下可能会出现缓存不一致的问题。但是,如果我们希望在缓存更新时进行同步操作,可以使用
sync
属性来实现。
//当方法被多个线程并发调用时,只有一个线程能够执行方法并更新缓存,其他线程会被阻塞,直到更新完成后才能继续执行。 @Cacheable(cacheNames = "myCache", key = "#id", sync = true) public User getUserById(Long id) { //.... } //注意:使用 `sync` 属性会增加缓存访问的时间和资源消耗,因此建议只在必要的情况下使用。另外,对于高并发场景,使用 `sync` 属性可能会导致性能问题,因此需要谨慎使用。
以下是常用的 SpEL 表达式及其说明:
表达式 说明 #root 代表被调用方法的参数列表 #root.target 代表被调用方法的目标对象 #root.caches 代表方法调用对应的缓存 #root.methodName 代表被调用方法的名称 #root.targetClass 代表被调用方法所在的类 #result 代表方法调用的返回结果(仅在 @Cacheable 和 @CachePut 注解中有效) #argument 代表方法的参数,例如 #a0 代表第一个参数,#p0 也代表第一个参数 T(...) 调用静态方法,比如 T(java.lang.Math).PI 方法调用 直接调用方法,比如 hasPermission('read') 集合访问 访问数组、列表、集合等元素,比如 list[0] 属性访问 访问对象的属性,比如 [user.name] @CachePut
@CachePut 注解用于缓存标注方法的返回值,不管缓存中是否存在相同的键值,通常用于增加或更新操作。在方法执行后,将执行结果缓存到指定的缓存中。
该注解可选参数:
- cacheNames:同
@Cacheable
。 - key:同
@Cacheable
。 - condition: 同
@Cacheable
。 - unless:同
@Cacheable
。
- cacheNames:同
@CacheEvict
@CacheEvict 注解用于标注方法执行后删除缓存中的数据,通常用于删除操作。
该注解可选参数:
- cacheNames:同
@Cacheable
。 - key:同
@Cacheable
。 - condition:同
@Cacheable
。 - allEntries:配合
cacheNames
一起使用,当allEntries
属性设置为true
时,表示清除缓存中缓存名称为cacheNames
的所有条目。allEntries
默认值为false
,表示只删除与该方法对应的缓存条目。- 例如:@CacheEvict(cacheNames = "k1",allEntries = true),会删除缓存中所有缓存名称为k1的键值对。
- beforeInvocation:当
beforeInvocation
属性设置为true
时,表示在方法执行之前清除缓存;即使方法执行出现异常,缓存仍然会被清除。当beforeInvocation
属性设置为false
(默认值)时,表示在方法执行之后清除缓存;如果方法执行出现异常,缓存不会被清除。
- cacheNames:同
四、 代码示例
添加pom依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
数据缓存
@Service @CacheConfig(cacheNames = "user") public class CacheService { @CachePut(key = "'userId:' + #p0.userId") public User addUser(User user){ System.out.println("添加成功:user = " + user); return user; } @Cacheable(key = "'userId:' + #p0") public User getUser(Integer userId){ User userFromDB = getUserFromDB(userId); System.out.println("查询成功: userId = " + userId); return userFromDB; } @CachePut(key = "'userId:' + #p0") public User update(Integer userId,User user){ updateUserInDB(userId,user); System.out.println("更新成功:userId = " + userId); return user; } private void updateUserInDB(Integer userId,User user) { user.setUsername("DB_user"); } private User getUserFromDB(Integer userId) { User user = new User(); user.setUserId(userId); user.setUsername("DB_user"); return user; } @CacheEvict(cacheNames = "k1",allEntries = true) public void del(int id){ } }
单元测试
@SpringBootTest public class CacheServiceTest { @Resource private CacheService cacheService; private User user; @BeforeEach void initUser(){ user = new User(); user.setUserId(1); user.setUsername("zhang san"); user.setNickname("xiao san"); user.setPhone("18788888888"); } @Test void addCacheTest(){ cacheService.addUser(user); } @Test void getCacheTest(){ User user = cacheService.getUser(1); System.out.println("user = " + user); } @Test void updateCacheTest(){ cacheService.update(1,user); } @Test void delCacheTest(){ cacheService.del(2); } @Autowired private RedisTemplate redisTemplate; @Test public void key() { // 这里将缓存key都捞出来 Set<String> keys = (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> { Set<byte[]> sets = connection.keys("k*".getBytes()); Set<String> ans = new HashSet<>(); assert sets != null; for (byte[] b : sets) { ans.add(new String(b)); } return ans; }); System.out.println("keys = " + keys); } }
总结
以上就是SpringBoot结合Redis实现缓存管理功能的详细内容,更多关于SpringBoot Redis缓存管理的资料请关注脚本之家其它相关文章!