
本项目代码:https://gitee.com/yunjiao-source/tutorials4j/tree/master/framework
在分布式系统中,缓存是提升性能的关键手段。Redis 作为高性能的键值存储,常与 Spring Cache 抽象结合使用。Spring Boot 提供了自动配置的 RedisCacheManager,但在实际业务中,我们往往需要为不同缓存设置独立的 TTL、前缀、是否允许空值等个性化配置。本文介绍一套精细化、可扩展的 Redis 缓存自定义方案,通过定制器模式和显式初始化,实现对缓存的全面管控。
Spring Boot 的 RedisCacheManager 默认使用统一的 RedisCacheConfiguration,所有缓存共享相同的 TTL、键前缀等。这无法满足多业务场景下的差异化需求。例如:
除了配置差异化,还存在一个隐性问题:RedisCacheManager 虽然实现了 InitializingBean,但在某些自动配置场景下,afterPropertiesSet 并不会自动执行,导致预定义的命名缓存配置无法被加载,运行时获取缓存会退化为默认配置。因此需要手动触发初始化。
本方案基于 Spring Boot 的扩展机制,提供以下核心组件:
组件 | 职责 |
|---|---|
| 承载全局和每个命名缓存的配置(TTL、KeyPrefix、cacheNullValues 等) |
| 在构建 |
| 在 |
| 工具类:生成带租户前缀的 |
| 集中管理 |
| 自动配置类,按条件注册上述 Bean |
整体流程如下图所示(文字描述):
CacheRedisProperties 配置。CacheRedisConfiguration 注册 NamedRedisCacheManagerBuilderCustomizer 及其他 Bean。RedisCacheManagerCreator 获取 RedisConnectionFactory,构建 RedisCacheManagerBuilder。RedisCacheConfiguration,并调用 builder.withInitialCacheConfigurations() 预置。RedisCacheManager 实例后,再通过 NamedCacheManagerCustomizer 调用 initializeCaches(),确保配置立即生效。为了方便配置,定义 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: 7200sRedisUtils 提供两个核心功能:
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;
}该类实现 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);
}CacheManagerCustomizer<RedisCacheManager> 会在 RedisCacheManager 实例创建后被调用。它仅做一件事:cacheManager.initializeCaches()。这确保所有通过 withInitialCacheConfigurations 预定义的缓存被立即初始化,而不是等到第一次访问时才懒加载,避免配置丢失。
@Override
public void customize(RedisCacheManager cacheManager) {
cacheManager.initializeCaches();
}为了避免分散的 Bean 定义导致创建顺序混乱,RedisCacheManagerCreator 作为 Supplier<RedisCacheManager> 集中处理:
RedisConnectionFactory 创建 Builder。properties)。RedisCacheManagerBuilderCustomizer(包括自定义的命名缓存定制器)。RedisCacheManager。CacheManagerCustomizer<RedisCacheManager>(包括初始化触发器)。该组件被注册为 Spring Bean,需要用到 RedisCacheManager 的地方可注入此 Supplier 或直接获取其生成的实例。
自动配置类使用 @ConditionalOnMissingBean 保证用户可覆盖默认实现。它注册了三个核心 Bean:
NamedRedisCacheManagerBuilderCustomizerNamedCacheManagerCustomizerRedisCacheManagerCreator同时,通过 ObjectProvider 收集所有实现 RedisCacheManagerBuilderCustomizer 和 CacheManagerCustomizer 的 Bean,传递给 Creator,形成可扩展的定制链。
在 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确保主类或配置类上标注 @EnableCaching。然后可以注入 RedisCacheManager:
@Autowired
private RedisCacheManager cacheManager;或者使用 RedisCacheManagerCreator 手动控制创建时机。
@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**?**undefined该方法只是声明了初始配置,实际 Cache 对象仍然是在第一次访问时才创建。initializeCaches() 强制提前创建,确保后续使用直接命中预置配置。RedisCacheManager 的关系undefined本方案完全兼容,只是通过定制器增强了默认行为。若不使用本配置,Spring Boot 仍会按原有方式工作。通过组合 RedisCacheManagerBuilderCustomizer 与 CacheManagerCustomizer,我们实现对 Redis 缓存管理器的精细控制,既满足了多业务场景的差异化配置需求,又解决了预置缓存初始化的问题。该方案已在实际项目中稳定运行,代码结构清晰,易于扩展。希望本文能帮助读者更好地理解和定制 Spring Cache + Redis 的缓存层。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。