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

目录
相关文章
|
2月前
|
前端开发 Java API
[049][Crypto模块]前后端混合加密API实战:基于Spring Boot的AES+RSA安全传输方案
本文详解Spring Boot中AES+RSA混合加密实战:前端用RSA公钥加密随机AES密钥并传输,后端通过`@Crypto`注解自动解密请求体。涵盖公钥分发、Hex/Base64编码统一、ECB模式适配及Caffeine缓存优化,提供开箱即用的端到端安全传输方案。(239字)
219 0
|
2月前
|
人工智能 API 数据库
Kimi深夜突袭K3,2.8万亿参数超大水桶!直接跨入世界第一梯队!
老金我昨儿在网上瞎逛的时候,突然看到了Kimi K3的消息。 合计先打开官网看看开发者文档有没有啥信儿,结果刚打开官网,好家伙。。就看到已经上线了! ![Image](https://ucc.alicdn.com/pic/developer-ecology/p3shvhj26rigq_0488c34b2827443e9b77df8ee1e3a066.png) Kimi官网Chat窗口里已
|
2月前
|
运维 Cloud Native 安全
企业远程运维方案选型:从第三方远控到云原生架构的演进思考
本文探讨企业远程运维从第三方工具向云原生架构(如阿里云无影)演进的路径,聚焦权限精细化、安全审计合规、TCO透明可控三大核心诉求,对比分析架构差异与生态集成优势,提供场景驱动、成本核算、实测验证的选型决策框架。(239字)
186 6
|
2月前
|
JSON 前端开发 Java
[048][Crypto模块]Spring Boot 请求体自动解密:@Crypto 注解 + RequestBodyAdvice 实现
本文介绍基于Spring Boot的请求体自动解密方案:通过自定义`@Crypto`注解与`RequestBodyAdvice`,在Controller入参前透明解密RSA/SM2加密的JSON请求体,实现业务代码零侵入、算法可插拔、开关灵活的安全传输机制。(239字)
163 1
|
2月前
|
前端开发 NoSQL Java
[027][Web模块]基于 Spring MVC 的 API 签名校验拦截器设计与实现
本文介绍基于Spring MVC的API签名校验拦截器,支持HmacSHA256签名、时间窗校验、nonce防重放及密钥动态加载,通过`@RequiredSignature`注解无侵入集成,具备高扩展性与生产可用性。(239字)
176 1
|
2月前
|
SQL Java 数据库连接
[026][数据模块]基于 MyBatis Plus 的企业级数据访问框架设计与实现
本文介绍基于MyBatis Plus二次封装的企业级数据访问框架,支持多租户隔离、分页、乐观锁、SQL防攻击及审计字段自动填充。通过有序拦截器链、条件化配置与开放扩展点,实现可插拔、易维护的统一数据访问能力。(239字)
142 2
|
2月前
|
算法 安全 Java
[047][Crypto模块]基于 Hutool 的常见加解密算法封装与密钥自动生成
本文基于Hutool封装统一加解密框架,提供AES/RSA/SM2/SM4/HMAC等算法的`CryptoProcessor`标准接口,支持密钥自动生成与动态切换,解耦业务代码,兼顾国密合规与易用性,提升安全性与可测试性。(239字)
123 1
|
2月前
|
缓存 NoSQL Redis
[031][缓存模块]RedisTemplate工具的租户隔离设计:自动Key前缀机制
本文介绍一种轻量级Redis缓存租户隔离方案:通过自定义`PrefixKeyStringRedisSerializer`,结合`TenantContextHolder`自动为所有缓存Key添加租户前缀(如`&quot;acme:users::user:123&quot;`),实现业务无感、零侵入的多租户数据隔离,兼顾安全性与复用性。(239字)
98 0
|
前端开发 Java 数据库连接
[030][Web模块]Spring Boot 验证与 OpenAPI 集成实战:从校验规则到文档生成
本文详解Spring Boot中Jakarta Validation与Springdoc OpenAPI的深度集成:通过自定义ModelResolver,将`@Min`、`@Pattern`及自定义注解(如`@CreditCardNumber`、`@LocalDateTimeFormat`)自动映射为OpenAPI扩展字段,实现校验规则与文档同步,提升API规范性与协作效率。(239字)
59 0
|
2月前
|
NoSQL 算法 Java
[050][功能模块]基于 Redis Bitmap 的高性能签到系统设计
本文详解基于Redis Bitmap的高性能签到系统设计,利用位图将用户月签到状态压缩至几字节,支持毫秒级签到、连续天数计算(BITFIELD)、日/月活统计等。代码开源,模块化强,支持多业务隔离与灵活扩展。(239字)
133 0