商城首页欢迎来到中国正版软件门户

您的位置:首页 >Spring Cache多缓存写入实现方法

Spring Cache多缓存写入实现方法

  发布于2026-04-16 阅读(0)

扫一扫,手机访问

Spring Cache 实现单次调用写入多缓存的完整方案

本文详解如何在 Spring 中通过 @Cacheable 结合 CacheManager 手动操作,实现一次业务调用将同一对象同时写入多个命名缓存(如按 customerID 和 accountNumber 双键缓存),避免重复远程调用并保证缓存一致性。

本文详解如何在 Spring 中通过 `@Cacheable` 结合 `CacheManager` 手动操作,实现一次业务调用将同一对象同时写入多个命名缓存(如按 customerID 和 accountNumber 双键缓存),避免重复远程调用并保证缓存一致性。

在基于 Spring 的微服务开发中,常遇到「一个实体可通过多种唯一标识查询」的场景——例如客户信息既可通过 customerId(Long 类型)获取,也可通过 accountNumber(String 类型)获取。理想情况下,首次任一方式查询后,应自动将结果同步写入两个缓存区,后续任一方式查询均可命中缓存,无需再次触发耗时的远程 API 调用。但 Spring 原生 @Cacheable 仅支持单缓存写入,无法直接满足“一写双存”需求。本文提供一种简洁、可靠且生产可用的解决方案。

✅ 核心思路:@Cacheable + CacheManager 显式双写

不依赖复杂 AOP 组合或自定义注解,而是利用 Spring 提供的 CacheManager 接口,在 @Cacheable 方法成功返回后,手动将结果写入另一个关联缓存。该方式逻辑清晰、调试友好、兼容所有 Spring Cache 实现(如 Caffeine、Redis、EhCache)。

? 示例实现

@Service
public class CachedIdentifiersCache {

    private final Cache identifiersCacheByCustomerId;
    private final Cache identifiersCacheByAccountNumber;
    private final LazyIdentifiersService slowServiceWithMultipleRestInvocations;

    public CachedIdentifiersCache(CacheManager cacheManager,
                                  LazyIdentifiersService remoteService) {
        this.slowServiceWithMultipleRestInvocations = remoteService;

        // 预加载两个缓存实例(确保缓存已声明,如 @EnableCaching + 配置)
        this.identifiersCacheByCustomerId = 
            Objects.requireNonNull(cacheManager.getCache("identifiers_cache_by_customer_id"));
        this.identifiersCacheByAccountNumber = 
            Objects.requireNonNull(cacheManager.getCache("identifiers_cache_by_account_number"));
    }

    @Cacheable(value = "identifiers_cache_by_customer_id", key = "#customerId")
    public Identifiers getCachedIdentifiers(Long customerId) {
        Identifiers identifiers = slowServiceWithMultipleRestInvocations.getIdentifiers();
        // ✅ 查询成功后,主动写入 accountNumber 缓存
        if (identifiers != null && identifiers.getAccountNumber() != null) {
            identifiersCacheByAccountNumber.put(identifiers.getAccountNumber(), identifiers);
        }
        return identifiers;
    }

    @Cacheable(value = "identifiers_cache_by_account_number", key = "#accountNumber")
    public Identifiers getCachedIdentifiers(String accountNumber) {
        Identifiers identifiers = slowServiceWithMultipleRestInvocations.getIdentifiers();
        // ✅ 查询成功后,主动写入 customerId 缓存
        if (identifiers != null && identifiers.getCustomerId() != null) {
            identifiersCacheByCustomerId.put(identifiers.getCustomerId(), identifiers);
        }
        return identifiers;
    }
}

? 注意:LazyIdentifiersService.getIdentifiers() 方法需适配参数(如接受 Long customerId 或 String accountNumber),实际中建议重构为统一查询入口(例如 findByIdentifier(Object identifier)),并通过运行时类型判断路由,此处为简化演示保留原始结构。

⚠️ 关键注意事项

  • 缓存名称必须预先配置:确保 identifiers_cache_by_customer_id 和 identifiers_cache_by_account_number 已在 @Configuration 类中通过 CacheManager 正确注册(如 Caffeine 需在 CaffeineCacheManager.setCacheNames(...) 中声明)。
  • 空值与 NPE 防御:务必检查 identifiers 及其字段(如 getAccountNumber()/getCustomerId())是否为 null,避免向缓存写入无效键。
  • 线程安全与并发问题:当多个线程同时请求同一客户(如线程 A 查 customerId=98765,线程 B 查 accountNumber="abc123"),仍可能触发两次远程调用。若需强一致性,可结合 @Cacheable(sync = true)(仅限单 JVM)或分布式锁(如 RedisLock)控制首次加载;但多数场景下“最终一致 + 缓存穿透防护”已足够。
  • 不推荐 @CachePut 组合方案:因 Spring AOP 代理限制,@Cacheable 方法内无法直接调用本类 @CachePut 方法(会导致绕过代理,缓存失效)。手动 Cache.put() 是更可控的选择。

✅ 总结

Spring Cache 本身不原生支持“一调双存”,但通过注入 CacheManager 并显式操作底层 Cache 实例,可在保持代码简洁的同时精准达成目标。该方案具备以下优势:

  • ✅ 零第三方依赖,纯 Spring Boot 原生能力;
  • ✅ 缓存逻辑集中、可测试性强(可 Mock Cache 对象验证 put 行为);
  • ✅ 易扩展:未来新增缓存维度(如 email、phone)只需增加对应 Cache 字段和写入逻辑;
  • ✅ 与缓存失效策略正交:@CacheEvict 仍可独立作用于任一缓存,互不影响。

只要合理设计缓存键结构与生命周期,这种“主缓存 + 辅助缓存”模式能显著提升多路径查询场景下的响应性能与系统健壮性。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注