SpringBoot结合Redis实现缓存管理功能

 更新时间:2024年01月29日 11:51:45   作者:棒棒糖_  
本篇文章主要介绍spring boot缓存管理机制及相关概念,以及如何结合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

      • cacheNamekey 可以组合成最终用于在 Redis 等缓存中存储数据的 key。通常情况下,cacheName 代表缓存的名称,而 key 则用于标识缓存中的具体数据。当使用 @Cacheable(cacheNames = "myCache", key = "#user.id") 这样的注解时,Spring 将会根据给定的 cacheNamekey 生成一个唯一的缓存 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
  • @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 (默认值)时,表示在方法执行之后清除缓存;如果方法执行出现异常,缓存不会被清除。

四、 代码示例

添加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缓存管理的资料请关注脚本之家其它相关文章!

相关文章

  • spring cloud eureka注册原理-注册失败填坑笔记

    spring cloud eureka注册原理-注册失败填坑笔记

    这篇文章主要介绍了spring cloud eureka注册原理-注册失败填坑笔记,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • SpringBoot 整合 Spring-Session 实现分布式会话项目实战

    SpringBoot 整合 Spring-Session 实现分布式会话项目实战

    本文主要介绍了SpringBoot 整合 Spring-Session 实现分布式会话项目实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Java继承关系中构造器的使用详解

    Java继承关系中构造器的使用详解

    本文介绍了继承关系中构造器的使用要点,创建子类对象时,必须先调用父类构造器实现初始化,若父类无参构造器被覆盖,子类需用super显式调用父类构造器,且super须写在构造器首行,感兴趣的朋友跟随小编一起看看吧
    2025-10-10
  • SpringBoot实现滑块验证码验证登陆校验功能详解

    SpringBoot实现滑块验证码验证登陆校验功能详解

    验证码作为一种自然人的机器人的判别工具,被广泛的用于各种防止程序做自动化的场景中。传统的字符型验证安全性已经名存实亡的情况下,各种新型的验证码如雨后春笋般涌现,今天给大家分享一篇SpringBoot实现滑块验证码
    2022-09-09
  • Java中的ReadWriteLock高效处理并发读写操作实例探究

    Java中的ReadWriteLock高效处理并发读写操作实例探究

    这篇文章主要为大家介绍了Java中的ReadWriteLock高效处理并发读写操作实例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Java实现简单的弹球游戏

    Java实现简单的弹球游戏

    这篇文章主要为大家详细介绍了Java实现简单的弹球游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • 解决Mybatis的serverTimezone时区出现问题

    解决Mybatis的serverTimezone时区出现问题

    这篇文章主要介绍了解决Mybatis的serverTimezone时区出现问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 使用eclipse 实现将springboot项目打成jar包

    使用eclipse 实现将springboot项目打成jar包

    这篇文章主要介绍了使用eclipse 实现将springboot项目打成jar包的流程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • shardingsphere-jdbc5.5.1分库分表实战

    shardingsphere-jdbc5.5.1分库分表实战

    本文主要介绍了shardingsphere-jdbc5.5.1分库分表实战,解决了分表和读写分离的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-01-01
  • SpringBoot项目部署的四种方式详解

    SpringBoot项目部署的四种方式详解

    本文详细介绍了SpringBoot项目的四种部署方式,包括原始命令部署、宝塔Linux可视化部署、Docker容器化部署和Docker容器平台托管,每种方式都有其特点和适用场景,需要的朋友可以参考下
    2026-04-04

最新评论