Spring Boot 3 整合 Spring Cache 与 Redis 缓存实战

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Spring Boot 3 整合 Spring Cache 与 Redis 缓存实战

什么是 Spring Cache?


Spring Cache是Spring框架提供的一层缓存抽象,旨在简化应用程序中的缓存管理。通过使用Spring Cache,开发者能够在方法级别方便地定义缓存策略,提高应用性能、响应速度,并减轻底层数据源的负载。该框架提供一系列注解,如@Cacheable、@CacheEvict、@CachePut,以及对多种底层缓存实现的支持,如EhCache、Redis等。它为应用程序提供了一种统一、方便、灵活的缓存管理方式,允许开发者通过简单的配置实现复杂的缓存逻辑,同时与Spring框架紧密集成。这种抽象层的存在使得在更改底层缓存框架时更为轻松,同时提供了一致的配置接口和更强大的高级特性。


Spring Cache 常用 API


@Cacheable


@Cacheable: 用于标记一个方法的结果应该被缓存。当在该方法被调用时,Spring首先检查缓存中是否已经有了预期的结果,如果有,直接返回缓存中的结果而不执行实际的方法体。

javaCopy code@Cacheable(cacheNames = "exampleCache", key = "'exampleKey'")
public String getCachedData() {
    // 执行实际的方法体,结果会被缓存
}

@CacheEvict


@CacheEvict: 用于标记一个方法在执行后清空缓存。可以指定清空的缓存名称和清空的条件。

javaCopy code@CacheEvict(cacheNames = "exampleCache", key = "'exampleKey'")
public void clearCache() {
    // 执行实际的方法体,之后清空缓存
}

@CachePut


@CachePut: 用于标记一个方法的结果应该被放入缓存,常用于在方法执行后更新缓存。

javaCopy code@CachePut(cacheNames = "exampleCache", key = "'exampleKey'")
public String updateCache() {
    // 执行实际的方法体,结果会被放入缓存
}

@Caching


@Caching: 允许同时应用多个缓存操作注解。

javaCopy code@Caching(
    evict = {
        @CacheEvict(cacheNames = "cache1", key = "'key1'"),
        @CacheEvict(cacheNames = "cache2", key = "'key2'")
    }
)
public void clearMultipleCaches() {
    // 执行实际的方法体,清空多个缓存
}

Spring Boot 整合 Spring Cache (Redis 缓存)


完整项目源码:youlai-boot


项目依赖 pom.xml

<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>

项目配置 application.yml

spring:
  data:
    redis:
      database: 6
      host: localhost
      port: 6379
      password: 123456
      timeout: 10s
      lettuce:
        pool:
          # 连接池最大连接数 默认8 ,负数表示没有限制
          max-active: 8
          # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
          max-wait: -1
          # 连接池中的最大空闲连接 默认8
          max-idle: 8
          # 连接池中的最小空闲连接 默认0
          min-idle: 0
  cache:
    # 缓存类型 redis、none(不使用缓存)
    type: redis
    # 缓存时间(单位:ms)
    redis:
      time-to-live: 3600000
      # 缓存null值,防止缓存穿透
      cache-null-values: true

自动装配配置类 RedisCacheConfig

package com.youlai.system.config;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
/**
 * Redis 缓存配置
 *
 * @author haoxr
 * @since 2023/12/4
 */
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
public class RedisCacheConfig {
    /**
     * 自定义 RedisCacheManager
     * <p>
     * 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
     *
     * @param redisConnectionFactory {@link RedisConnectionFactory}
     * @param cacheProperties        {@link CacheProperties}
     * @return {@link RedisCacheManager}
     */
    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory, CacheProperties cacheProperties){
        return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration(cacheProperties))
                .build();
    }
    /**
     * 自定义 RedisCacheConfiguration
     *
     * @param cacheProperties {@link CacheProperties}
     * @return {@link RedisCacheConfiguration}
     */
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        config = config.computePrefixWith(name -> name + ":");// 覆盖默认key双冒号  CacheKeyPrefix#prefixed
        return config;
    }
}

Spring Boot 路由缓存实战


完整项目源码:youlai-boot


获取路由数据缓存


获取路由列表,通过 @Cacheable 注解,将方法返回的路由列表数据缓存至 Redis。当再次请求时,不再进入方法体,而是直接从 Redis 中读取缓存数据并返回,以提高性能。

    /**
     * 获取路由列表
     */
    @Override
    @Cacheable(cacheNames = "menu", key = "'routes'") // cacheNames 为必填项,key 需要使用引号,否则会被识别为变量。
    public List<RouteVO> listRoutes() {
        List<RouteBO> menuList = this.baseMapper.listRoutes();
        return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList);
    }

10.png

更新路由缓存失效


若需要在路由信息更新时使缓存失效,可以使用 @CacheEvict 注解,它用于在方法执行之后(默认)从缓存中移除条目。

    /**
     * 新增/修改菜单
     */
    @Override
    @CacheEvict(cacheNames = "menu", key = "'routes'",beforeInvocation = true)
    public boolean saveMenu(MenuForm menuForm) {
        String path = menuForm.getPath();
        MenuTypeEnum menuType = menuForm.getType();
        // 如果是目录
        if (menuType == MenuTypeEnum.CATALOG) {
            if (menuForm.getParentId() == 0 && !path.startsWith("/")) {
                menuForm.setPath("/" + path); // 一级目录需以 / 开头
            }
            menuForm.setComponent("Layout");
        }
        // 如果是外链
        else if (menuType == MenuTypeEnum.EXTLINK) {
            menuForm.setComponent(null);
        }
        SysMenu entity = menuConverter.form2Entity(menuForm);
        String treePath = generateMenuTreePath(menuForm.getParentId());
        entity.setTreePath(treePath);
        return this.saveOrUpdate(entity);
    }

更新菜单后,Redis 中缓存的路由数据将被清除。再次获取路由时,才会将新的路由数据缓存到 Redis 中。


Spring Cache 问题整理


@Cacheable 缓存的 key 双冒号


默认情况下 @Cacheable 使用双冒号 :: 拼接 cacheNames 和 key

@Cacheable(cacheNames = "menu", key = "'routes'") 

11.png

参考源码 RedisCacheConfiguration#prefixCacheNameWith

12.png13.png如果需要将双冒号改成单个冒号,需要重写 RedisCacheConfiguration#computePrefixWith 方法

config = config.computePrefixWith(name -> name + ":");//覆盖默认key双冒号  CacheKeyPrefix#prefixed

14.png

结语


Spring Cache 为 Spring 应用程序提供了简便的缓存管理抽象,通过注解如 @Cacheable、@CacheEvict、@CachePut,开发者能方便定义缓存策略。整合Spring Boot 与 Redis 缓存实例展示了如何配置、使用Spring Cache,提升应用性能。通过实战演示菜单路由缓存,突显 @Cacheable 和 @CacheEvict 的实际应用,以及解决 @Cacheable 默认key双冒号的问题。


开源项目


SpringCloud + Vue3 微服务商城


SpringBoot 3+ Vue3 单体权限管理系统

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5天前
|
缓存 NoSQL Java
SpringBoot实现缓存预热的几种常用方案
SpringBoot实现缓存预热的几种常用方案
|
5天前
|
监控 Java 应用服务中间件
spring和springboot的区别
spring和springboot的区别
15 1
|
4天前
|
存储 缓存 NoSQL
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
11 0
|
4天前
|
Java Maven Docker
0.07 秒启动一个 SpringBoot 项目!Spring Native 很强!!
0.07 秒启动一个 SpringBoot 项目!Spring Native 很强!!
17 2
|
4天前
|
缓存 NoSQL Redis
深度解析Redis的缓存双写一致性
【4月更文挑战第20天】
29 1
|
5天前
|
存储 缓存 NoSQL
Redis入门到通关之Redis缓存数据实战
Redis入门到通关之Redis缓存数据实战
14 0
|
6天前
|
Java Maven 数据库
Spring Boot Starter: 快速简明地创建Spring应用
Spring Boot Starter: 快速简明地创建Spring应用
|
6天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
7天前
|
存储 缓存 运维
软件体系结构 - 缓存技术(5)Redis Cluster
【4月更文挑战第20天】软件体系结构 - 缓存技术(5)Redis Cluster
137 10
|
2月前
|
缓存 NoSQL 安全
【Redis】缓存穿透
【Redis】缓存穿透
30 0