[005][缓存模块]Redis自定义缓存:基于Spring Boot的精细化缓存管理实践
本项目代码:gitee.com/yunjiao-sou…
在分布式系统中,缓存是提升性能的关键手段。Redis 作为高性能的键值存储,常与 Spring Cache 抽象结合使用。Spring Boot 提供了自动配置的 RedisCacheManager,但在实际业务中,我们往往需要为不同缓存设置独立的 TTL、前缀、是否允许空值等个性化配置。本文介绍一套精细化、可扩展的 Redis 缓存自定义方案,通过定制器模式和显式初始化,实现对缓存的全面管控。
一、为什么需要自定义 Redis 缓存?
Spring Boot 的 RedisCacheManager 默认使用统一的 RedisCacheConfiguration,所有缓存共享相同的 TTL、键前缀等。这无法满足多业务场景下的差异化需求。例如:
- 用户会话缓存希望 30 分钟过期
- 商品详情缓存希望 1 小时过期
- 字典数据希望永不过期(或很长)
除了配置差异化,还存在一个隐性问题:RedisCacheManager 虽然实现了 InitializingBean,但在某些自动配置场景下,afterPropertiesSet 并不会自动执行,导致预定义的命名缓存配置无法被加载,运行时获取缓存会退化为默认配置。因此需要手动触发初始化。
二、整体设计思路
本方案基于 Spring Boot 的扩展机制,提供以下核心组件:
| 组件 | 职责 |
|---|---|
CacheRedisProperties | 承载全局和每个命名缓存的配置(TTL、KeyPrefix、cacheNullValues 等) |
NamedRedisCacheManagerBuilderCustomizer | 在构建 RedisCacheManager 时,将命名缓存配置转换为独立的 RedisCacheConfiguration |
NamedCacheManagerCustomizer | 在 RedisCacheManager 创建后,强制调用 initializeCaches(),确保预置缓存生效 |
RedisUtils | 工具类:生成带租户前缀的 CacheKeyPrefix、合并配置 |
RedisCacheManagerCreator | 集中管理 RedisCacheManager 的创建过程,整合所有定制器 |
CacheRedisConfiguration | 自动配置类,按条件注册上述 Bean |
整体流程如下图所示(文字描述):
- 应用启动,加载
CacheRedisProperties配置。 CacheRedisConfiguration注册NamedRedisCacheManagerBuilderCustomizer及其他 Bean。RedisCacheManagerCreator获取RedisConnectionFactory,构建RedisCacheManagerBuilder。- 定制器遍历命名缓存,为每个缓存生成独立的
RedisCacheConfiguration,并调用builder.withInitialCacheConfigurations()预置。 - 构建
RedisCacheManager实例后,再通过NamedCacheManagerCustomizer调用initializeCaches(),确保配置立即生效。
三、核心组件详解
3.1 配置属性类(CacheRedisProperties)
为了方便配置,定义 CacheRedisProperties 和 RedisOptions
tutorials4j:
cache:
redis:
enable-statistics: true
time-to-live: 3600s # 全局默认TTL
cache-null-values: false # 全局是否缓存null
use-key-prefix: true
key-prefix: "" # 全局前缀(实际还会叠加租户)
named-caches:
users:
time-to-live: 1800s
cache-null-values: true
key-prefix: "user"
products:
time-to-live: 7200s
3.2 RedisUtils – 键前缀与配置填充
RedisUtils 提供两个核心功能:
- 多租户前缀:
defaultCacheKeyPrefix()结合TenantContextHolder,自动生成包含租户标识的完整前缀,例如tenantA:users::。 - 配置合并:
fillConfiguration()将RedisOptions中的 TTL、空值允许、自定义前缀等属性应用到RedisCacheConfiguration。
关键代码片段:
static CacheKeyPrefix defaultCacheKeyPrefix() {
return name -> TenantContextHolder.get() + ":" + name + "::";
}
static RedisCacheConfiguration fillConfiguration(RedisCacheConfiguration configuration,
RedisOptions prop) {
if (prop.getTimeToLive() != null) {
configuration = configuration.entryTtl(prop.getTimeToLive());
}
if (!prop.isCacheNullValues()) {
configuration = configuration.disableCachingNullValues();
}
if (prop.isUseKeyPrefix() && StringUtils.isNotBlank(prop.getKeyPrefix())) {
configuration = configuration.computePrefixWith(defaultCacheKeyPrefix(prop.getKeyPrefix()));
}
return configuration;
}
3.3 NamedRedisCacheManagerBuilderCustomizer – 构建期定制
该类实现 RedisCacheManagerBuilderCustomizer,在 RedisCacheManagerBuilder 构建阶段介入。它从 properties 中读取 namedCaches 映射,为每个缓存名创建独立的 RedisCacheConfiguration(基于全局默认配置覆盖个性化参数),最后调用 builder.withInitialCacheConfigurations(configMap) 预置。
@Override
public void customize(RedisCacheManager.RedisCacheManagerBuilder builder) {
if (CollectionUtils.isEmpty(properties.getNamedCaches())) {
return;
}
RedisCacheConfiguration defaultConfig = builder.cacheDefaults();
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
properties.getNamedCaches().forEach((name, options) -> {
RedisCacheConfiguration customConfig = RedisUtils.fillConfiguration(defaultConfig, options);
configMap.put(name, customConfig);
});
builder.withInitialCacheConfigurations(configMap);
}
3.4 NamedCacheManagerCustomizer – 初始化触发器
CacheManagerCustomizer<RedisCacheManager> 会在 RedisCacheManager 实例创建后被调用。它仅做一件事:cacheManager.initializeCaches()。这确保所有通过 withInitialCacheConfigurations 预定义的缓存被立即初始化,而不是等到第一次访问时才懒加载,避免配置丢失。
@Override
public void customize(RedisCacheManager cacheManager) {
cacheManager.initializeCaches();
}
3.5 RedisCacheManagerCreator – 聚合创建逻辑
为了避免分散的 Bean 定义导致创建顺序混乱,RedisCacheManagerCreator 作为 Supplier<RedisCacheManager> 集中处理:
- 从
RedisConnectionFactory创建 Builder。 - 设置全局默认配置(来自
properties)。 - 应用所有
RedisCacheManagerBuilderCustomizer(包括自定义的命名缓存定制器)。 - 构建
RedisCacheManager。 - 应用所有
CacheManagerCustomizer<RedisCacheManager>(包括初始化触发器)。 - 返回单例实例。
该组件被注册为 Spring Bean,需要用到 RedisCacheManager 的地方可注入此 Supplier 或直接获取其生成的实例。
3.6 自动配置类 CacheRedisConfiguration
自动配置类使用 @ConditionalOnMissingBean 保证用户可覆盖默认实现。它注册了三个核心 Bean:
NamedRedisCacheManagerBuilderCustomizerNamedCacheManagerCustomizerRedisCacheManagerCreator
同时,通过 ObjectProvider 收集所有实现 RedisCacheManagerBuilderCustomizer 和 CacheManagerCustomizer 的 Bean,传递给 Creator,形成可扩展的定制链。
四、如何使用这套自定义方案
步骤1:引入依赖与配置
在 application.yml 中添加 Redis 连接信息及缓存配置:
spring:
redis:
host: localhost
port: 6379
tutorials4j:
cache:
redis:
enable-statistics: true
time-to-live: 3600s
cache-null-values: false
named-caches:
userCache:
time-to-live: 1800s
cache-null-values: true
key-prefix: "user"
productCache:
time-to-live: 7200s
步骤2:启用缓存并注入 RedisCacheManager
确保主类或配置类上标注 @EnableCaching。然后可以注入 RedisCacheManager:
@Autowired
private RedisCacheManager cacheManager;
或者使用 RedisCacheManagerCreator 手动控制创建时机。
步骤3:使用 @Cacheable 等注解
@Cacheable(value = "userCache", key = "#userId")
public User getUser(Long userId) { ... }
此时 userCache 将使用独立的 1800s TTL、允许缓存 null,且键前缀为 {租户}:user:userCache::。
五、核心要点总结
- 差异化配置:通过
named-caches结合RedisCacheManagerBuilderCustomizer,为不同缓存注入独立配置。 - 强制初始化:通过
CacheManagerCustomizer调用initializeCaches(),确保预置缓存配置立即生效,避免懒加载导致配置不一致。 - 租户隔离:
RedisUtils中的defaultCacheKeyPrefix自动从TenantContextHolder获取租户标识,实现多租户数据天然隔离。 - 可扩展性:提供
@ConditionalOnMissingBean,用户可覆盖或新增定制器,无需修改框架代码。 - 生命周期整合:
RedisCacheManagerCreator作为单一入口,保证定制器的执行顺序(先 BuilderCustomizer,后 CacheManagerCustomizer)。
六、常见问题
-
为什么不能只靠
withInitialCacheConfigurations?
该方法只是声明了初始配置,实际 Cache 对象仍然是在第一次访问时才创建。initializeCaches()强制提前创建,确保后续使用直接命中预置配置。 -
是否支持动态增减缓存?
可以的,运行时新增缓存使用默认属性配置。 -
与 Spring Boot 默认
RedisCacheManager的关系
本方案完全兼容,只是通过定制器增强了默认行为。若不使用本配置,Spring Boot 仍会按原有方式工作。
七、结语
通过组合 RedisCacheManagerBuilderCustomizer 与 CacheManagerCustomizer,我们实现对 Redis 缓存管理器的精细控制,既满足了多业务场景的差异化配置需求,又解决了预置缓存初始化的问题。该方案已在实际项目中稳定运行,代码结构清晰,易于扩展。希望本文能帮助读者更好地理解和定制 Spring Cache + Redis 的缓存层。