游乐游手机版
首页/AI热点日报/热点详情

多级命名缓存个性化配置设计与实现

类型:热点整理2026-07-17
命名缓存:多级个性化缓存配置的设计与实现 在业务系统日益复杂的背景下,不同类型缓存对容量、过期时间、键前缀等参数的要求各不相同。例如,用户信息缓存通常需要较长的 TTL,而验证码缓存只需几分钟即过期。传统的统一缓存配置已无法满足这种多样化的需求。因此,命名缓存(Named Cache)方案应运而生,

命名缓存:多级个性化缓存配置的设计与实现

在业务系统日益复杂的背景下,不同类型缓存对容量、过期时间、键前缀等参数的要求各不相同。例如,用户信息缓存通常需要较长的 TTL,而验证码缓存只需几分钟即过期。传统的统一缓存配置已无法满足这种多样化的需求。因此,命名缓存(Named Cache)方案应运而生,它允许开发者通过配置文件为每个缓存名称独立定义策略,并同时支持 Redis 和 Caffeine 两种后端实现,从而显著提升缓存管理的灵活性与可维护性。

一、命名缓存的概念

命名缓存的核心思想是:在同一个缓存管理器内,依据缓存名称的不同,动态应用各异的缓存配置(例如 TTL、是否允许缓存空值、键前缀、初始容量等)。开发者仅需在配置文件中按 caches.<缓存名称> 格式声明参数,缓存管理器在创建对应缓存时便会自动采用这些个性化设定。

这种设计带来以下好处:

  • 精细控制:针对不同业务场景精准调整缓存行为,避免“一刀切”策略。
  • 配置友好:通过外部配置文件即可修改,无需改动代码,降低运维成本。
  • 实现透明:开发时仍使用 Spring 标准的 @Cacheable 注解,只需指定正确的缓存名称,底层自动适配。

小提示:命名缓存的设计精髓在于“默认配置 + 差异化覆盖”,开发者只需在配置文件中声明需要覆盖的字段,其余字段自动继承默认值,从而大幅减少配置冗余。

二、整体配置模型

2.1 配置属性类

项目将 NamedCacheProperties 定义为核心配置载体,其结构如下所示:

@Data
@ConfigurationProperties(prefix = "tutorials4j.cache.named")
public class NamedCacheProperties {   private NamedCacheOptions defaults = new NamedCacheOptions();  private Map caches = new HashMap<>();
}

  • defaults:全局默认配置,所有缓存均继承此配置(除非被单独覆盖)。
  • caches:键为缓存名称,值为对应缓存的特有配置。

NamedCacheOptions 定义了通用选项以及后端特有选项:

public class NamedCacheOptions {   private Duration timeToLive;  // 过期时间  private Boolean cacheNullValues;// 是否缓存null值  private Boolean enableStatistics;     // 是否启用统计
  private RedisOptions redis = new RedisOptions(); // Redis专属配置  private CaffeineOptions caffeine = new CaffeineOptions(); // Caffeine专属配置
}

其中 RedisOptions 包含 keyPrefix 键前缀;CaffeineOptions 则包含 initialCapacitymaximumSizeexpireAfterAccess 等参数。

每个 NamedCacheOptions 均可通过调用 applyDefaults(NamedCacheOptions defaults) 方法,将未设置的属性从默认配置中合并,从而完美实现“默认配置 + 差异化覆盖”的模型。

2.2 配置示例

application.yml 中:

tutorials4j:cache:  named:
defaults:
time-to-live: 60s
cache-null-values: false
redis:key-prefix: "default:"
caches:
userCache:time-to-live: 300scache-null-values: trueredis:  key-prefix: "user:"
productCache:time-to-live: 600scaffeine:  initial-capacity: 500  maximum-size: 20000

此时,userCache 会覆盖 TTL 和空值缓存策略,并使用自定义键前缀;productCache 则沿用默认 TTL,但修改了 Caffeine 的容量参数。

常见问题:如果配置文件中未定义某个缓存名称,是否会使用默认配置?答案是肯定的,因为默认配置适用于所有缓存,除非在 caches 项下单独定义,否则将自动采用 defaults 中的设置。

三、Redis 命名缓存的实现

3.1 缓存管理器创建器

RedisCacheManagerCreator 负责创建 RedisCacheManager 实例,并通过双重检查锁单例模式确保同一配置下只有一个管理器。其 newInstance() 方法用于构建 RedisCacheManager.Builder 链。

public RedisCacheManager newInstance() {   RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig();  defaultConfig = RedisUtils.fillConfiguration(defaultConfig, properties.getDefaults());  RedisCacheManager.RedisCacheManagerBuilder builder = 
RedisCacheManager.builder(factory).cacheDefaults(defaultConfig);  // 应用所有 RedisCacheManagerBuilderCustomizer  redisCacheManagerBuilderCustomizer.forEach(c -> c.customize(builder));  // 应用所有 CacheManagerCustomizer  RedisCacheManager redisCacheManager = builder.build();  cacheManagerCustomizer.forEach(c -> c.customize(redisCacheManager));  return redisCacheManager;
}

3.2 命名缓存配置的注入

关键实现是 NamedRedisCacheManagerBuilderCustomizer,它实现了 RedisCacheManagerBuilderCustomizer 接口,会在 builder 构建前被调用。该类遍历 properties.getCaches(),为每个命名缓存创建独立的 RedisCacheConfiguration,然后通过 builder.withInitialCacheConfigurations(configMap) 一次性注入所有配置。

@Override
public void customize(RedisCacheManager.RedisCacheManagerBuilder builder) {   RedisCacheConfiguration defaultConfig = builder.cacheDefaults();  Map configMap = new HashMap<>();  properties.getCaches().forEach((name, options) -> { 
RedisCacheConfiguration namedConfig = RedisUtils.fillConfiguration(defaultConfig, options);
configMap.put(name, namedConfig);  });  builder.withInitialCacheConfigurations(configMap);
}

RedisUtils.fillConfiguration 方法依据 NamedCacheOptions 中的 timeToLivecacheNullValueskeyPrefix(通过 RedisOptions)修改默认配置。这样一来,当应用首次操作 userCache 时,RedisCacheManager 便会自动采用该缓存专属的配置。

3.3 自动装配

CacheRedisConfiguration 中条件化地注册了上述定制器和创建器:

@Bean(CacheManagerCreatorCategory.REDIS_CREATOR)
@ConditionalOnMissingBean
RedisCacheManagerCreator redisCacheManagerCreator(...) {  ... }

此外,还注册了 JSON 序列化定制器等(与命名缓存主题无关的内容此处略过)。

小提示:RedisCacheManagerBuilderCustomizer 是一个强大的扩展点,如果需要自定义序列化方式或连接池,也可以通过实现该接口来注入自定义配置。

四、Caffeine 命名缓存的实现

Caffeine 作为进程内缓存,其命名缓存实现思路与 Redis 类似,但需要直接控制 CaffeineCacheManager 的缓存创建逻辑。

4.1 自定义缓存管理器

FlexibleCaffeineCacheManager 继承自 CaffeineCacheManager,重写了 createNativeCaffeineCache(String name) 方法:

@Override
protected Cache createNativeCaffeineCache(String name) {   Map optionsMap = properties.getCaches();  if (optionsMap.containsKey(name)) { 
NamedCacheOptions options = optionsMap.get(name);
options.applyDefaults(properties.getDefaults());  // 合并默认配置
Caffeine caffeine = Caffeine.newBuilder();
CaffeineUtils.copyOption(caffeine, options);// 应用 TTL、容量等
return caffeine.build();  }  return super.createNativeCaffeineCache(name);
}

关键步骤:

  1. 根据缓存名称查找是否存在专用配置。
  2. 调用 applyDefaults 方法,将未设置的属性从全局默认值中补全。
  3. 创建一个新的 Caffeine 构造器,并通过 CaffeineUtils.copyOption 设置过期时间、容量、访问过期等参数。
  4. 调用 build() 方法生成原生 Cache 实例。

这样,每个命名缓存都将拥有独立的 Caffeine 对象,其大小、过期策略互不干扰。

4.2 缓存管理器创建器

CaffeineCacheManagerCreator 同样采用单例模式,其 newInstance() 方法直接创建 FlexibleCaffeineCacheManager,并注入全局 Caffeine 实例(该实例基于 properties.getDefaults() 构建,作为后备默认值)。

public CaffeineCacheManager newInstance() {   FlexibleCaffeineCacheManager caffeineCacheManager = new FlexibleCaffeineCacheManager(properties);  caffeineCacheManager.setCaffeine(caffeine);  return caffeineCacheManager;
}

4.3 自动装配

CacheCaffeineConfiguration 负责创建默认 Caffeine Bean 和 CaffeineCacheManagerCreator Bean。同时,FlexibleCaffeineCacheManager 内部已处理命名缓存的配置,因此无需额外注册 CacheManagerCustomizer

常见问题:如果多个缓存名称共享同一个 Caffeine 实例,如何保证隔离性?通过重写 createNativeCaffeineCache 方法,每个名称都会创建独立的 Cache 实例,从而实现完全隔离,互不影响。

五、使用示例

假设我们有一个业务服务,需要同时使用 Redis 缓存用户信息,使用 Caffeine 缓存产品目录:

@Service
public class UserService {   @Cacheable(value = "userCache", key = "#userId")  public User getUser(Long userId) {  ... }
}

@Service
public class ProductService {   @Cacheable(value = "productCache", key = "#productId")  public Product getProduct(Long productId) {  ... }
}

配置文件(application.yml)如下:

tutorials4j.cache.named:defaults:  time-to-live: 60s  cache-null-values: false  redis:
key-prefix: "global:"caches:  userCache:
time-to-live: 300s
cache-null-values: true
redis.key-prefix: "user:"  productCache:
time-to-live: 600s
caffeine:
initial-capacity: 200
maximum-size: 10000
expire-after-access: 300s

运行时:

  • userCache 操作 Redis 缓存:TTL 设置为 300 秒,允许缓存 null 值,键前缀为 user:
  • productCache 使用 Caffeine 缓存:最大容量 10000 条记录,初始容量 200,访问后 300 秒过期。

开发者只需关注 @Cacheable 注解中的缓存名称,底层管理器会自动应用对应的策略配置。

六、总结

命名缓存机制通过统一的配置模型将缓存名称与具体参数解耦,极大地提升了缓存管理的灵活性和可维护性。本文分别以 Redis 和 Caffeine 为例,展示了:

  • 如何使用 NamedCachePropertiesNamedCacheOptions 来描述差异化的缓存配置。
  • 如何为 Redis 利用 RedisCacheManagerBuilderCustomizer 注入不同的缓存配置。
  • 如何为 Caffeine 通过重写 createNativeCaffeineCache 方法为每个缓存名称创建独立实例。

这种设计可以轻松扩展至其他缓存提供者(如 EhCache),核心思想始终不变:让配置跟随缓存名称动态变化,而非全局僵化。在微服务或多业务模块的架构中,命名缓存是一项值得参考的基础设施实践。

来源:https://developer.aliyun.com/article/1748414

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。