[028][缓存模块]命名缓存:多级个性化缓存配置的设计与实现

简介: 本文介绍“命名缓存”(Named Cache)设计方案:通过配置文件为不同缓存名称(如userCache、productCache)独立设置TTL、空值缓存、键前缀、容量等策略,支持Redis与Caffeine双后端。配置即生效,无需修改代码,兼顾灵活性与可维护性。(239字)

[028][缓存模块]命名缓存:多级个性化缓存配置的设计与实现

本文章代码: gitee , gitcode , github

在复杂的业务系统中,不同缓存往往有着不同的容量、过期时间、键前缀等需求。例如,用户信息缓存可能需要较长的 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<String, NamedCacheOptions> 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: 300s
          cache-null-values: true
          redis:
            key-prefix: "user:"
        productCache:
          time-to-live: 600s
          caffeine:
            initial-capacity: 500
            maximum-size: 20000

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

三、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 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<String, RedisCacheConfiguration> 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 序列化定制器等(与命名缓存主题无关的略述)。

四、Caffeine 命名缓存的实现

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

4.1 自定义缓存管理器

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

@Override
protected Cache<Object, Object> createNativeCaffeineCache(String name) {
   
    Map<String, NamedCacheOptions> optionsMap = properties.getCaches();
    if (optionsMap.containsKey(name)) {
   
        NamedCacheOptions options = optionsMap.get(name);
        options.applyDefaults(properties.getDefaults());  // 合并默认配置
        Caffeine<Object, Object> 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

五、使用示例

假设我们有一个业务服务,需要同时使用 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),核心思想始终不变:让配置跟随缓存名称动态变化,而非全局僵化。在微服务或多业务模块的架构中,命名缓存是一项值得参考的基础设施实践。

目录
相关文章
|
3月前
|
缓存 安全 搜索推荐
[004][缓存模块]Caffeine缓存自定义:构建灵活的Spring Boot缓存管理器
本文介绍Spring Boot中Caffeine缓存的灵活定制方案:通过自定义`FlexibleCaffeineCacheManager`,支持按缓存名(如users/products)独立配置过期策略、容量等参数,兼顾全局默认与个性化需求;结合线程安全创建器、属性合并机制及无缝Spring集成,实现高性能、易扩展、零侵入的本地缓存管理。(239字)
217 2
|
3月前
|
缓存 NoSQL Java
[006][缓存模块] 两级缓存实战:基于 Caffeine + Redis 的多级缓存设计与实现
本文介绍基于Caffeine(本地)+ Redis(分布式)的两级缓存实战方案,通过自定义`MultiLevelCache`与`MultiLevelCacheManager`,实现Spring Cache标准接口下的透明多级缓存:读优先本地(纳秒级)、未命中查Redis并回填;写同步更新两级,兼顾高性能与数据共享。代码开源可直接集成。
325 0
|
1月前
|
人工智能 API 数据库
Kimi深夜突袭K3,2.8万亿参数超大水桶!直接跨入世界第一梯队!
老金我昨儿在网上瞎逛的时候,突然看到了Kimi K3的消息。 合计先打开官网看看开发者文档有没有啥信儿,结果刚打开官网,好家伙。。就看到已经上线了! ![Image](https://ucc.alicdn.com/pic/developer-ecology/p3shvhj26rigq_0488c34b2827443e9b77df8ee1e3a066.png) Kimi官网Chat窗口里已
|
XML JSON Java
SpringMVC中HttpMessageConverter使用实践详解
SpringMVC中HttpMessageConverter使用实践详解
1201 0
|
30天前
|
存储 缓存 NoSQL
[051][缓存模块]基于 StringRedisTemplate 的多租户 Key 隔离设计与实践——以 RedisBitmapUtils 为例
本文介绍基于StringRedisTemplate的多租户Redis Key隔离方案:通过自定义TenantStringRedisSerializer,在Key序列化时自动注入租户前缀,实现透明、低侵入的租户数据隔离。以RedisBitmapUtils为例,业务代码无需感知租户ID,所有操作自动适配,兼顾安全性与易用性。(239字)
107 0
|
1月前
|
运维 Cloud Native 安全
企业远程运维方案选型:从第三方远控到云原生架构的演进思考
本文探讨企业远程运维从第三方工具向云原生架构(如阿里云无影)演进的路径,聚焦权限精细化、安全审计合规、TCO透明可控三大核心诉求,对比分析架构差异与生态集成优势,提供场景驱动、成本核算、实测验证的选型决策框架。(239字)
154 6
|
1月前
|
JSON 前端开发 Java
[048][Crypto模块]Spring Boot 请求体自动解密:@Crypto 注解 + RequestBodyAdvice 实现
本文介绍基于Spring Boot的请求体自动解密方案:通过自定义`@Crypto`注解与`RequestBodyAdvice`,在Controller入参前透明解密RSA/SM2加密的JSON请求体,实现业务代码零侵入、算法可插拔、开关灵活的安全传输机制。(239字)
123 1
|
1月前
|
消息中间件 负载均衡 算法
大模型GPU推理队列排队治理:限流规则+优先级调度+长短拆分+集群负载指南.172
大模型推理队列是承载生成请求的缓冲调度层,用于应对GPU算力稀缺、推理耗时长且不均等特性。通过限流、优先级调度、长短请求拆分、溢出防护和集群负载均衡五大治理手段,实现削峰填谷、保障高可用与资源高效利用。
166 2
|
1月前
|
前端开发 NoSQL Java
[027][Web模块]基于 Spring MVC 的 API 签名校验拦截器设计与实现
本文介绍基于Spring MVC的API签名校验拦截器,支持HmacSHA256签名、时间窗校验、nonce防重放及密钥动态加载,通过`@RequiredSignature`注解无侵入集成,具备高扩展性与生产可用性。(239字)
143 1
|
1月前
|
SQL Java 数据库连接
[026][数据模块]基于 MyBatis Plus 的企业级数据访问框架设计与实现
本文介绍基于MyBatis Plus二次封装的企业级数据访问框架,支持多租户隔离、分页、乐观锁、SQL防攻击及审计字段自动填充。通过有序拦截器链、条件化配置与开放扩展点,实现可插拔、易维护的统一数据访问能力。(239字)
119 2