sky-take-out项目中Redis的使用示例详解

 更新时间:2025年07月23日 11:37:03   作者:iam_leeqing  
SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现Redis的高级功能(如数据结构、事务、分布式锁),本文给大家介绍sky-take-out项目中Redis的使用,感兴趣的朋友一起看看吧

Spring Cache 是 Spring Framework 提供的一个抽象层,用于简化应用程序中的缓存管理。它允许开发者以声明式的方式将缓存添加到应用中,而不需要手动编写冗长的缓存逻辑代码。Spring Cache 通过提供一系列注解和配置选项,使得集成缓存变得非常简单。将 Redis 集成到 Spring 应用程序中,并将其作为 Spring Cache 的缓存提供者。这样做的好处是可以在开发过程中专注于业务逻辑的实现,只需通过 Spring Cache 提供的简单注解即可完成复杂的缓存管理任务。与此同时,你还能享受到 Redis 提供的高效性能和丰富的功能特性。这种方法不仅提高了开发效率,还增强了应用的可扩展性和性能表现。

Spring Cache主要特性

  1. 声明式缓存:通过使用注解(如 @Cacheable, @CachePut, @CacheEvict 等),可以非常方便地在方法上定义缓存行为。
  2. 多种缓存实现支持:Spring Cache 抽象层支持多种缓存解决方案,包括但不限于 Ehcache, Hazelcast, Infinispan, JCache (JSR-107), Guava Cache, Caffeine, Redis 等。
  3. 灵活的配置:可以通过 XML 配置、Java Config 或者自动配置来设置缓存管理器(Cache Manager)。
  4. 条件缓存:可以根据特定条件决定是否执行缓存操作(例如,使用 condition 属性)。
  5. SpEL 支持:可以在注解中使用 Spring Expression Language (SpEL) 来动态指定缓存键或其他属性值。

核心注解

1.@Cacheable

此注解通常用于查询操作,当调用被注解的方法时,如果缓存中存在对应的数据,则直接返回缓存结果,而不是执行方法体。

    @GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    @Cacheable(cacheNames = "setmealCache",key = "#categoryId")
    public Result<List<Setmeal>> list(Long categoryId) {
        Setmeal setmeal = new Setmeal();
        setmeal.setCategoryId(categoryId);
        setmeal.setStatus(StatusConstant.ENABLE);
        List<Setmeal> list = setmealService.list(setmeal);
        return Result.success(list);
    }
  • value: 指定缓存的名字。
  • key: 可选参数,用来指定缓存的键。可以使用 SpEL 表达式。

2.@CachePut

@Cacheable 不同,@CachePut 总是会执行方法,并将方法的结果放入缓存中。适用于更新操作。

    @GetMapping("/{id}")
    @ApiOperation("根据id查询套餐")
    @CachePut(cacheNames = "setmealCache",key = "#categoryId")
    public Result<SetmealVO> getById(@PathVariable Long id) {
        SetmealVO setmealVO = setmealService.getByIdWithDish(id);
        return Result.success(setmealVO);
    }

3.@CacheEvict

用于清除缓存,适用于删除或无效化数据的操作。

    @PostMapping
    @ApiOperation("新增套餐")
    @CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
    public Result save(@RequestBody SetmealDTO setmealDTO) {
        setmealService.saveWithDish(setmealDTO);
        return Result.success();
    }
  • value: 指定缓存的名字。
  • key: 用来指定缓存的键。可以使用 SpEL 表达式。
@CacheEvict(cacheNames = "setmealCache", allEntries = true)
public Result startOrStop(@PathVariable Integer status, Long id) {
    setmealService.startOrStop(status, id);
    return Result.success();
}

@CacheEvict(cacheNames = "setmealCache", allEntries = true) 被用来清除名为 setmealCache 的缓存中的所有条目。

  • cacheNames/value:指定要操作的缓存名称。在这个例子中是 "setmealCache"。这两个属性是等价的,用 cacheNames 或者 value 来指定缓存名。

  • allEntries:一个布尔值,默认为 false。如果设置为 true,则表示删除指定缓存中的所有条目。若为 false,则根据 key 属性(如果没有提供 key,则默认为空)来决定删除哪个具体的缓存项。在例子中,由于 allEntries = true,所以无论是否有特定的键被定义,都会清空整个 setmealCache 缓存。

  • beforeInvocation:一个布尔值,默认为 false。如果设置为 true,那么缓存的清除会在方法调用之前发生;否则,在方法成功执行之后才会进行清除操作。这有助于处理方法执行过程中可能发生的异常情况,避免不必要的缓存清除。

  • 使用 allEntries = true 会一次性清除指定缓存中的所有条目,这对于缓存命中率和性能有一定的影响,特别是在缓存非常大的情况下。因此,应该谨慎使用,并确保这是必要的操作。

不是所有 Redis 的功能都可以通过 Spring Cache 注解实现。

✅Spring Cache 注解能做什么?

Spring Cache 的设计初衷是简化缓存操作,提供一种统一的、声明式的缓存管理方式,适用于常见的缓存场景,比如:

  • 缓存方法结果(@Cacheable
  • 更新缓存(@CachePut
  • 删除缓存(@CacheEvict
  • 组合多个缓存操作(@Caching

这些注解非常适合用于:

  • 读多写少的业务场景(如查询接口)
  • 需要缓存方法返回值的场景
  • 避免重复执行耗时操作(如数据库查询)

❌Spring Cache 注解不能做什么?

虽然 Spring Cache 很方便,但它是一个抽象层,只封装了缓存操作中最常见的部分。它并不能覆盖 Redis 所有的强大功能。以下是一些 Spring Cache 注解无法实现难以实现的功能:

Redis 功能Spring Cache 注解是否支持
字符串、哈希、列表、集合、有序集合等数据结构操作❌ 不支持,需使用 RedisTemplateStringRedisTemplate
发布/订阅消息(Pub/Sub)❌ 不支持
Lua 脚本执行❌ 不支持
分布式锁(SETNX / RedLock)❌ 不支持
HyperLogLog、Geo、Bitmap 等高级数据结构❌ 不支持
批量操作(Pipeline)❌ 不支持
事务(MULTI / EXEC)❌ 不支持
TTL、KEYS、SCAN 等管理命令❌ 不支持
自定义缓存过期策略(比如不同 key 有不同的 TTL)⚠️ 部分支持,但不够灵活

🛠️举个例子:

场景:缓存用户信息

@Cacheable("user")
public User getUserById(Long id) {
    return userRepository.findById(id);
}

✅ 适合用 Spring Cache,因为这是个典型的“查数据库缓存结果”的场景。

场景:实现一个分布式锁

Boolean success = redisTemplate.opsForValue().setIfAbsent("lock_key", "locked", 30, TimeUnit.SECONDS);

❌ 无法用 Spring Cache 实现,必须用 RedisTemplate 直接操作 Redis。

✅什么时候用 Spring Cache?

  • 你只需要缓存方法的返回值。
  • 你希望代码更简洁,不想写大量缓存逻辑。
  • 你希望统一管理缓存策略(如过期时间、缓存名称)。
  • 你未来可能更换缓存实现(比如从 Redis 换成 Caffeine)。

❌什么时候要直接使用 RedisTemplate?

  • 你需要使用 Redis 的高级功能(如 Hash、List、Pub/Sub、Lua、分布式锁等)。
  • 你需要更细粒度地控制 Redis 操作(如 Pipeline、事务)。
  • 你需要实现一些缓存抽象层无法覆盖的特殊逻辑。

✅最佳实践:结合使用

在实际项目中,推荐结合使用 Spring Cache 和 RedisTemplate

  • 对于通用的缓存需求(如方法结果缓存),使用 Spring Cache 注解。
  • 对于需要 Redis 高级特性的场景,使用 RedisTemplate 直接操作 Redis。

🧠 总结一句话:

Spring Cache 注解只能覆盖 Redis 的部分缓存功能,不能替代 Redis 的全部能力。
对于复杂的 Redis 操作,仍需使用 RedisTemplate

如果正在开发一个中大型项目,建议:

  • 对通用缓存逻辑使用 Spring Cache 注解;
  • 对 Redis 高级功能单独封装成 Redis 工具类或服务类,使用 RedisTemplate 来实现。

这样既能享受注解带来的便捷,又能保留 Redis 的灵活性和强大功能。

1. 字符串、哈希、列表、集合、有序集合等数据结构操作

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 主要用于缓存方法的结果,并不直接支持对 Redis 的各种数据类型(如字符串、哈希、列表、集合和有序集合)的操作。

解决方案:
使用 RedisTemplateStringRedisTemplate 来进行更复杂的 Redis 数据类型操作。

示例:字符串操作

@Autowired
private StringRedisTemplate stringRedisTemplate;
public void stringOperationsExample(String key, String value) {
    // 设置字符串值
    stringRedisTemplate.opsForValue().set(key, value);
    // 获取字符串值
    String retrievedValue = stringRedisTemplate.opsForValue().get(key);
    System.out.println("Retrieved Value: " + retrievedValue);
}

示例:哈希操作

public void hashOperationsExample(String key, String field, String value) {
    // 设置哈希表中的字段值
    stringRedisTemplate.opsForHash().put(key, field, value);
    // 获取哈希表中字段的值
    Object fieldValue = stringRedisTemplate.opsForHash().get(key, field);
    System.out.println("Field Value: " + fieldValue);
}

示例:列表操作

public void listOperationsExample(String key, String... values) {
    // 右侧插入元素到列表
    for (String value : values) {
        stringRedisTemplate.opsForList().rightPush(key, value);
    }
    
    // 获取列表中的所有元素
    List<String> elements = stringRedisTemplate.opsForList().range(key, 0, -1);
    
    System.out.println("List Elements: " + elements);
}

示例:集合操作

public void setOperationsExample(String key, String... members) {
    // 添加成员到集合
    for (String member : members) {
        stringRedisTemplate.opsForSet().add(key, member);
    }
    // 获取集合中的所有成员
    Set<String> membersSet = stringRedisTemplate.opsForSet().members(key);
    System.out.println("Members of Set: " + membersSet);
}

示例:有序集合操作

public void sortedSetOperationsExample(String key, String member, double score) {
    // 添加成员到有序集合并指定分数
    stringRedisTemplate.opsForZSet().add(key, member, score);
    // 获取有序集合中的所有成员及其分数
    Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().rangeWithScores(key, 0, -1);
    tuples.forEach(tuple -> System.out.println("Member: " + tuple.getValue() + ", Score: " + tuple.getScore()));
}

2. 发布/订阅消息(Pub/Sub)

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 不支持发布/订阅模式。

解决方案:
使用 RedisTemplate 进行发布/订阅操作。

@Autowired
private StringRedisTemplate stringRedisTemplate;
// 订阅者
public void subscribeToChannel(String channelName) {
    stringRedisTemplate.getConnectionFactory().getConnection().subscribe((message, pattern) -> {
        System.out.println("Received message on channel [" + new String(pattern) + "] with content: " + new String(message.getBody()));
    }, channelName.getBytes());
}
// 发布者
public void publishToChannel(String channelName, String message) {
    stringRedisTemplate.convertAndSend(channelName, message);
}

3. Lua 脚本执行

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 不支持执行 Lua 脚本。

解决方案:
使用 RedisTemplate 执行 Lua 脚本。

@Autowired
private StringRedisTemplate stringRedisTemplate;
public void executeLuaScript() {
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua_script.lua")));
    redisScript.setResultType(Long.class);
    List<String> keys = Arrays.asList("key1", "key2");
    Long result = stringRedisTemplate.execute(redisScript, keys, "arg1");
    System.out.println("Lua Script Result: " + result);
}

4. 分布式锁(SETNX / RedLock)

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 不支持分布式锁机制。

解决方案:
使用 RedisTemplate 实现分布式锁。

@Autowired
private StringRedisTemplate stringRedisTemplate;
public boolean acquireLock(String lockKey, long timeout) throws InterruptedException {
    long millisecondsTimeout = System.currentTimeMillis() + timeout;
    while (System.currentTimeMillis() < millisecondsTimeout) {
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "locked", Duration.ofMinutes(1));
        if (Boolean.TRUE.equals(success)) {
            return true;
        }
        Thread.sleep(100); // 等待一段时间后重试
    }
    return false;
}
public void releaseLock(String lockKey) {
    stringRedisTemplate.delete(lockKey);
}

5. HyperLogLog、Geo、Bitmap 等高级数据结构

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 不支持这些高级数据结构。

解决方案:
使用 RedisTemplate 操作这些高级数据结构。

Geo 操作示例:

public void geoOperationsExample() {
    stringRedisTemplate.opsForGeo().add("cities",
        new Point(13.361389, 38.115556), "Palermo",
        new Point(15.087269, 37.502669), "Catania");
    Distance distance = stringRedisTemplate.opsForGeo().distance("cities", "Palermo", "Catania", RedisGeoCommands.DistanceUnit.KILOMETERS);
    System.out.println("Distance between Palermo and Catania: " + distance.getValue() + " km");
}

6. 批量操作(Pipeline)

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 不支持批量操作。

解决方案:
使用 RedisTemplate 进行批量操作。

public void pipelineOperationsExample() {
    List<Object> results = stringRedisTemplate.executePipelined((RedisCallback<String>) connection -> {
        connection.openPipeline();
        connection.set("key1".getBytes(), "value1".getBytes());
        connection.incr("key2".getBytes());
        return null;
    });
    System.out.println("Pipeline Results: " + results);
}

7. 事务(MULTI / EXEC)

Spring Cache 注解的支持情况:

  • 不支持。Spring Cache 不支持事务操作。

解决方案:
使用 RedisTemplate 进行事务操作。

public void transactionOperationsExample() {
    stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
        @Override
        public List<Object> execute(RedisOperations operations) throws DataAccessException {
            operations.multi();
            operations.opsForValue().increment("key1");
            operations.opsForValue().increment("key2");
            return operations.exec(); // 执行并返回结果
        }
    });
}

8. TTL、KEYS、SCAN 等管理命令

Spring Cache 注解的支持情况:

  • 部分支持。虽然可以设置全局的 TTL,但不能为每个键单独设置不同的 TTL。

解决方案:
使用 RedisTemplate 设置 TTL 和执行其他管理命令。

public void ttlOperationsExample(String key) {
    // 设置TTL
    stringRedisTemplate.expire(key, Duration.ofMinutes(10));
}
public void scanKeysExample() {
    ScanOptions options = ScanOptions.scanOptions().match("*").count(100).build();
    Cursor<Map.Entry<byte[], byte[]>> cursor = stringRedisTemplate.executeWithStickyConnection(
        redisConnection -> new ConvertingCursor<>(redisConnection.scan(options),
            entry -> new AbstractMap.SimpleEntry<>(new String(entry.getKey()), new String(entry.getValue()))));
    while (cursor.hasNext()) {
        Map.Entry<String, String> entry = cursor.next();
        System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
    }
}

通过上述例子可以看出,虽然 Spring Cache 提供了便捷的方式来管理缓存,但对于 Redis 的复杂功能,需要依赖 RedisTemplateStringRedisTemplate 来实现。这种方式不仅保留了 Redis 的灵活性,同时也使得开发者能够充分利用 Redis 的强大功能。

到此这篇关于sky-take-out项目中Redis的使用的文章就介绍到这了,更多相关sky-take-out redis使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringMVC如何域对象共享数据

    SpringMVC如何域对象共享数据

    在Spring MVC中,可以使用域对象来共享数据,域对象是一个Map类型的对象,可以在请求处理方法之间共享数据,本文给大家介绍SpringMVC 域对象共享数据的示例代码,一起看看吧
    2023-09-09
  • spring定时器@Scheduled异步调用方式

    spring定时器@Scheduled异步调用方式

    在Spring Boot中,@Schedule默认使用单线程执行定时任务,多个定时器会按顺序执行,为实现异步执行,可以通过自定义线程池或实现SchedulingConfigurer接口,使用自定义线程池可以保证多个定时器并发执行
    2024-11-11
  • 浅谈Spring Boot 2.0迁移指南主要注意点

    浅谈Spring Boot 2.0迁移指南主要注意点

    Spring官方的Spring Boot 2变动指南,主要是帮助您将应用程序迁移到Spring Boot 2.0,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • java map转Multipart/form-data类型body实例

    java map转Multipart/form-data类型body实例

    这篇文章主要介绍了java map转Multipart/form-data类型body实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • 详解如何修改idea配置文件位置从C盘更改到D盘

    详解如何修改idea配置文件位置从C盘更改到D盘

    这篇文章主要给大家介绍了关于如何将idea的配置文件从默认的C盘调整到D盘,从而节省C盘使用空间,具有很好的参考价值,希望对大家有所帮助,需要的朋友可以参考下
    2023-10-10
  • Java基础面试题之volatile详解

    Java基础面试题之volatile详解

    Volatile可以看做是轻量级的 Synchronized,它只保证了共享变量的可见性,下面这篇文章主要给大家介绍了关于Java基础面试题之volatile的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 解决Maven项目加载spring bean的配置xml文件会提示找不到问题

    解决Maven项目加载spring bean的配置xml文件会提示找不到问题

    这篇文章主要介绍了解决Maven项目加载spring bean的配置xml文件会提示找不到问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Spring学习笔记之RedisTemplate的配置与使用教程

    Spring学习笔记之RedisTemplate的配置与使用教程

    这篇文章主要给大家介绍了关于Spring学习笔记之RedisTemplate配置与使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用spring具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-06-06
  • Java实现Dijkstra输出最短路径的实例

    Java实现Dijkstra输出最短路径的实例

    这篇文章主要介绍了Java实现Dijkstra输出最短路径的实例的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-09-09
  • Java日期时间格式化操作DateUtils 的整理

    Java日期时间格式化操作DateUtils 的整理

    这篇文章主要介绍了Java日期时间格式化操作DateUtils 的整理的相关资料,这里总结了java日期格式化的操作,需要的朋友可以参考下
    2017-07-07

最新评论