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+Jpa多数据源配置的方法示例

    spring+Jpa多数据源配置的方法示例

    这篇文章主要介绍了spring+Jpa多数据源配置的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 将对象转化为字符串的java实例

    将对象转化为字符串的java实例

    这篇文章主要介绍了将对象转化为字符串的java实例,有需要的朋友可以参考一下
    2013-12-12
  • 详谈java中int和Integer的区别及自动装箱和自动拆箱

    详谈java中int和Integer的区别及自动装箱和自动拆箱

    这篇文章主要介绍了详谈java中int和Integer的区别及自动装箱和自动拆箱,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • java使用es查询的示例代码

    java使用es查询的示例代码

    本篇文章主要介绍了java使用es查询的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • JavaWeb会话技术详解与案例

    JavaWeb会话技术详解与案例

    会话技术:在Web开发中,服务器跟踪用户信息的奇数称为会话技术。会话:指的是一个客户端与服务器发生的一系列请求和响应的过程。由于请求包含的信息,在请求被销毁后也就不存在,多次让用户输入账号密码,会影响用户的使用体验感,基于此,产生了cookie和session技术
    2021-11-11
  • Java xml出现错误 javax.xml.transform.TransformerException: java.lang.NullPointerException

    Java xml出现错误 javax.xml.transform.TransformerException: java.

    这篇文章主要介绍了Java xml出现错误 javax.xml.transform.TransformerException: java.lang.NullPointerException的相关资料,需要的朋友可以参考下
    2016-11-11
  • Java阻塞队列BlockingQueue详解

    Java阻塞队列BlockingQueue详解

    这篇文章主要介绍了Java阻塞队列BlockingQueue,文章通过队列的类型展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-07-07
  • JVM系列之String.intern的性能解析

    JVM系列之String.intern的性能解析

    这篇文章主要介绍了JVM系列之String.intern的性能解析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • Java集合继承体系详解

    Java集合继承体系详解

    这篇文章主要为大家详细介绍了Java集合继承体系,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • 深入理解Java原生的序列化机制

    深入理解Java原生的序列化机制

    Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。下面小编和大家来一起学习一下吧
    2019-06-06

最新评论