SpringBoot 3 + Caffeine 企业级缓存最佳实践方案(一)

SpringBoot 3 + Caffeine 企业级缓存最佳实践方案(一)

SpringBoot 3 + Caffeine 企业级缓存最佳实践方案(二)

文章目录

  • 一、架构设计与核心配置
    • [1. 架构设计](#1. 架构设计)
      • [1.1 多级缓存架构设计](#1.1 多级缓存架构设计)
        • [1.1.1 整体架构图](#1.1.1 整体架构图)
        • [1.1.2 缓存读取流程](#1.1.2 缓存读取流程)
        • [1.1.3 缓存写入流程](#1.1.3 缓存写入流程)
      • [1.2 缓存分层策略](#1.2 缓存分层策略)
        • [1.2.1 缓存分层矩阵](#1.2.1 缓存分层矩阵)
        • [1.2.2 缓存类型定义](#1.2.2 缓存类型定义)
      • [1.3 缓存同步机制设计](#1.3 缓存同步机制设计)
        • [1.3.1 基于Redis Pub/Sub的同步方案](#1.3.1 基于Redis Pub/Sub的同步方案)
        • [1.3.2 缓存同步消息模型](#1.3.2 缓存同步消息模型)
      • [1.4 缓存更新策略](#1.4 缓存更新策略)
        • [1.4.1 四种主流更新策略对比](#1.4.1 四种主流更新策略对比)
        • [1.4.2 Cache Aside 模式(推荐)](#1.4.2 Cache Aside 模式(推荐))
        • [1.4.3 主动更新与被动失效](#1.4.3 主动更新与被动失效)
    • [2. 技术选型与依赖](#2. 技术选型与依赖)
      • [2.1 核心技术栈](#2.1 核心技术栈)
      • [2.2 Maven依赖配置](#2.2 Maven依赖配置)
    • [3. 核心配置实现](#3. 核心配置实现)
      • [3.1 项目结构](#3.1 项目结构)
      • [3.2 主启动类](#3.2 主启动类)
      • [3.3 Caffeine核心配置](#3.3 Caffeine核心配置)
      • [3.4 配置属性类](#3.4 配置属性类)
      • [3.5 异步执行器配置](#3.5 异步执行器配置)
  • 二、核心功能实现与高级特性
    • [1. 自定义缓存注解](#1. 自定义缓存注解)
      • [1.1 增强型缓存注解定义](#1.1 增强型缓存注解定义)
        • [1.1.1 @EnhancedCacheable 注解](#1.1.1 @EnhancedCacheable 注解)
        • [1.1.2 @EnhancedCacheEvict 注解](#1.1.2 @EnhancedCacheEvict 注解)
        • [1.1.3 @EnhancedCachePut 注解](#1.1.3 @EnhancedCachePut 注解)
      • [1.2 缓存注解处理器](#1.2 缓存注解处理器)
      • [1.3 注解使用示例](#1.3 注解使用示例)
    • [2. 缓存Key生成策略](#2. 缓存Key生成策略)
      • [2.1 Key生成器接口](#2.1 Key生成器接口)
      • [2.2 默认Key生成器实现](#2.2 默认Key生成器实现)
      • [2.3 自定义Key生成器](#2.3 自定义Key生成器)
    • [3. 缓存加载器实现](#3. 缓存加载器实现)
      • [3.1 异步缓存加载器](#3.1 异步缓存加载器)
      • [3.2 批量缓存加载器](#3.2 批量缓存加载器)
    • [4. 缓存防护机制](#4. 缓存防护机制)
      • [4.1 缓存穿透防护](#4.1 缓存穿透防护)
        • [4.1.1 布隆过滤器实现](#4.1.1 布隆过滤器实现)
        • [4.1.2 空值缓存策略](#4.1.2 空值缓存策略)
      • [4.2 缓存击穿防护](#4.2 缓存击穿防护)
      • [4.3 缓存雪崩防护](#4.3 缓存雪崩防护)
    • [5. 分布式缓存一致性](#5. 分布式缓存一致性)
      • [5.1 缓存同步管理器](#5.1 缓存同步管理器)
      • [5.2 缓存失效通知](#5.2 缓存失效通知)
    • [6. 缓存预热机制](#6. 缓存预热机制)
      • [6.1 预热数据加载器](#6.1 预热数据加载器)
      • [6.2 预热任务调度](#6.2 预热任务调度)

文档目录

  • 一:架构设计与核心配置 二:核心功能实现与高级特性 (本文档)
  • 三:监控运维与实战案例 四:性能优化与最佳实践

一、架构设计与核心配置

1. 架构设计

1.1 多级缓存架构设计

1.1.1 整体架构图
复制代码
┌─────────────────────────────────────────────────────────────────┐
│                      应用层 (Application Layer)                  │
│                    Controllers / Services                         │
└────────────────────────────┬────────────────────────────────────┘
                             │
┌────────────────────────────▼────────────────────────────────────┐
│                    缓存抽象层 (Cache Abstraction)                │
│              Spring Cache Abstraction + 自定义注解               │
│  @EnhancedCacheable | @EnhancedCacheEvict | @EnhancedCachePut   │
└────────────────────────────┬────────────────────────────────────┘
                             │
┌────────────────────────────▼────────────────────────────────────┐
│                  缓存管理层 (Cache Management Layer)              │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │          L1: Caffeine 本地缓存 (进程内)                  │   │
│  │  ┌──────────┬──────────┬──────────┬──────────┐         │   │
│  │  │ 热点数据 │ 常规数据 │ 冷数据   │ 限流缓存 │         │   │
│  │  │ 100ms   │ 5min    │ 30min   │ 1s      │         │   │
│  │  │ 10000条 │ 5000条  │ 1000条  │ 10000条 │         │   │
│  │  └──────────┴──────────┴──────────┴──────────┘         │   │
│  │  - 超高性能 (ns级响应)                                  │   │
│  │  - 零网络开销                                           │   │
│  │  - JVM堆内存存储                                        │   │
│  └─────────────────────────────────────────────────────────┘   │
│                             │                                    │
│                             │ Cache Miss / Sync                 │
│                             ▼                                    │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │          L2: Redis 分布式缓存 (跨进程)                   │   │
│  │  - 集群共享                                              │   │
│  │  - 持久化支持                                            │   │
│  │  - 跨节点同步                                            │   │
│  │  - 缓存预热数据                                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│                             │                                    │
│                             │ Pub/Sub 消息                      │
│                             ▼                                    │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │          缓存同步组件 (Cache Sync)                       │   │
│  │  - Redis Pub/Sub 消息订阅                                │   │
│  │  - 节点间缓存失效通知                                    │   │
│  │  - 最终一致性保证                                        │   │
│  └─────────────────────────────────────────────────────────┘   │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             │ Cache Miss
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                   数据访问层 (Data Access Layer)                 │
│                    Repository / DAO                              │
│                    MySQL / PostgreSQL / MongoDB                  │
└─────────────────────────────────────────────────────────────────┘
1.1.2 缓存读取流程
复制代码
用户请求
   │
   ▼
┌─────────────────┐
│  L1: Caffeine   │
│  本地缓存查询    │
└────┬────────────┘
     │
     ├─ 命中 ──────────────────────────┐
     │                                  │
     └─ 未命中                          │
        │                               │
        ▼                               │
   ┌─────────────────┐                 │
   │  L2: Redis      │                 │
   │  分布式缓存查询  │                 │
   └────┬────────────┘                 │
        │                               │
        ├─ 命中 ─────────┐              │
        │                │              │
        │            回填L1缓存          │
        │                │              │
        └─ 未命中         │              │
           │             │              │
           ▼             │              │
      ┌─────────────┐   │              │
      │  数据库查询  │   │              │
      └─────┬───────┘   │              │
            │           │              │
         写入L1缓存      │              │
            │           │              │
         写入L2缓存      │              │
            │           │              │
            └───────────┴──────────────┘
                        │
                        ▼
                    返回结果
1.1.3 缓存写入流程
复制代码
数据更新请求
      │
      ▼
┌──────────────┐
│  更新数据库   │
└──────┬───────┘
       │
       ▼
┌──────────────────────────┐
│  缓存更新策略选择         │
└──────┬───────────────────┘
       │
       ├─────────────┬─────────────┬──────────────┐
       │             │             │              │
       ▼             ▼             ▼              ▼
  Cache Aside   Write Through  Write Behind   Refresh Ahead
  (删除缓存)    (同步更新)     (异步写入)     (提前刷新)
       │             │             │              │
       ▼             ▼             ▼              ▼
  删除L1缓存    更新L1缓存    队列缓冲更新   异步预加载
       │             │             │              │
       ▼             ▼             ▼              ▼
  删除L2缓存    更新L2缓存    批量写数据库   定时刷新
       │             │             │              │
       ▼             ▼             ▼              ▼
  发布失效消息  发布更新消息  延迟持久化     保持热度
       │             │             │              │
       └─────────────┴─────────────┴──────────────┘
                     │
                     ▼
            其他节点同步更新/删除本地缓存

1.2 缓存分层策略

1.2.1 缓存分层矩阵
缓存层级 访问频率 命中率目标 容量 TTL 适用场景
热点数据层 极高 (>1000 QPS) >95% 10,000+ 100s-300s 秒杀商品、热门文章、活跃用户
常规数据层 中等 (100-1000 QPS) >85% 5,000 5min-10min 普通商品、用户信息、配置数据
冷数据层 较低 (<100 QPS) >70% 1,000 30min-1h 历史订单、归档数据、长尾商品
限流数据层 极高 (瞬时) N/A 10,000+ 1s-60s 接口限流、防刷控制、验证码
字典数据层 稳定 >90% 2,000 1h-24h 枚举配置、地区数据、系统参数
1.2.2 缓存类型定义
java 复制代码
/**
 * 缓存类型枚举
 * 定义不同业务场景的缓存策略
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Getter
@RequiredArgsConstructor
public enum CacheType {
    
    /**
     * 热点数据缓存
     * - 访问频率极高(命中率 > 80%)
     * - 短过期时间,快速刷新
     * - 大容量配置
     * 
     * 使用场景:
     * - 秒杀商品缓存
     * - 热门文章/视频
     * - 实时榜单数据
     * - 活跃用户会话
     */
    HOT_DATA("hotData", 10000, 100, 300, true),
    
    /**
     * 常规数据缓存
     * - 访问频率中等(命中率 50-80%)
     * - 中等过期时间
     * - 中等容量配置
     * 
     * 使用场景:
     * - 普通商品详情
     * - 用户基础信息
     * - 文章内容
     * - 订单详情
     */
    NORMAL_DATA("normalData", 5000, 300, 600, true),
    
    /**
     * 冷数据缓存
     * - 访问频率较低(命中率 < 50%)
     * - 长过期时间
     * - 小容量配置
     * 
     * 使用场景:
     * - 历史订单
     * - 归档数据
     * - 长尾商品
     * - 冷门内容
     */
    COLD_DATA("coldData", 1000, 1800, 3600, false),
    
    /**
     * 用户信息缓存
     * - 用户会话相关数据
     * - 中等过期时间
     * 
     * 使用场景:
     * - 用户个人资料
     * - 用户权限信息
     * - 用户偏好设置
     */
    USER_INFO("userInfo", 5000, 600, 1800, true),
    
    /**
     * 字典数据缓存
     * - 系统配置、枚举等静态数据
     * - 长过期时间
     * - 主动刷新
     * 
     * 使用场景:
     * - 地区数据
     * - 枚举配置
     * - 系统参数
     * - 分类树
     */
    DICTIONARY("dictionary", 2000, 3600, 7200, true),
    
    /**
     * 接口限流缓存
     * - 短时间窗口统计
     * - 极短过期时间
     * 
     * 使用场景:
     * - API限流
     * - 防刷控制
     * - 验证码校验
     * - IP黑名单
     */
    RATE_LIMIT("rateLimit", 10000, 1, 60, false),
    
    /**
     * 商品信息缓存
     * - 电商业务核心数据
     * - 中等过期时间
     * - 异步刷新
     * 
     * 使用场景:
     * - 商品详情
     * - 库存信息
     * - 价格数据
     * - SKU属性
     */
    PRODUCT_INFO("productInfo", 8000, 300, 900, true);
    
    private final String cacheName;          // 缓存名称
    private final int maximumSize;           // 最大容量
    private final int expireAfterWrite;      // 写入后过期时间(秒)
    private final int expireAfterAccess;     // 访问后过期时间(秒)
    private final boolean refreshAfterWrite;  // 是否启用异步刷新
}

1.3 缓存同步机制设计

1.3.1 基于Redis Pub/Sub的同步方案
复制代码
节点A                    Redis                    节点B                    节点C
  │                       │                        │                        │
  │ 1. 更新数据           │                        │                        │
  ├──────────────────────>│                        │                        │
  │                       │                        │                        │
  │ 2. 删除本地缓存       │                        │                        │
  │                       │                        │                        │
  │ 3. 发布失效消息       │                        │                        │
  ├──────────────────────>│                        │                        │
  │  Topic: cache:evict   │                        │                        │
  │  Data: {cacheName,    │                        │                        │
  │         key,          │                        │                        │
  │         nodeId}       │                        │                        │
  │                       │                        │                        │
  │                       │ 4. 订阅并接收消息      │                        │
  │                       ├───────────────────────>│                        │
  │                       ├────────────────────────┼───────────────────────>│
  │                       │                        │                        │
  │                       │ 5. 过滤自身消息        │                        │
  │                       │    (nodeId不同)        │                        │
  │                       │                        │                        │
  │                       │                   6. 删除本地缓存           6. 删除本地缓存
  │                       │                        │                        │
  │                       │                   7. 返回ACK              7. 返回ACK
  │                       │<───────────────────────│                        │
  │                       │<────────────────────────────────────────────────│
  │                       │                        │                        │
  │<──────8. 同步完成─────┤                        │                        │
1.3.2 缓存同步消息模型
java 复制代码
/**
 * 缓存同步消息
 * 用于节点间缓存数据同步
 */
@Data
@Builder
public class CacheSyncMessage implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    /**
     * 操作类型
     */
    private OperationType operationType;
    
    /**
     * 缓存名称
     */
    private String cacheName;
    
    /**
     * 缓存Key
     */
    private String cacheKey;
    
    /**
     * 缓存值(仅用于更新操作)
     */
    private Object cacheValue;
    
    /**
     * 发送节点ID(避免循环处理)
     */
    private String sourceNodeId;
    
    /**
     * 消息时间戳
     */
    private LocalDateTime timestamp;
    
    /**
     * Key前缀(用于批量删除)
     */
    private String keyPrefix;
    
    /**
     * 操作类型枚举
     */
    public enum OperationType {
        EVICT,              // 删除单个缓存
        EVICT_ALL,          // 删除所有缓存
        EVICT_BY_PREFIX,    // 按前缀删除
        UPDATE              // 更新缓存
    }
}

1.4 缓存更新策略

1.4.1 四种主流更新策略对比
策略 适用场景 优点 缺点 一致性 实现复杂度
Cache Aside 通用场景 简单可靠 短暂不一致 最终一致
Write Through 强一致性要求 一致性好 性能较差 强一致 ⭐⭐⭐
Write Behind 高并发写入 性能最优 可能丢数据 最终一致 ⭐⭐⭐⭐
Refresh Ahead 热点数据 命中率高 资源消耗大 弱一致 ⭐⭐⭐
1.4.2 Cache Aside 模式(推荐)
java 复制代码
/**
 * Cache Aside 模式实现
 * 读操作:先读缓存,未命中再读数据库,然后写入缓存
 * 写操作:先更新数据库,再删除缓存
 */
public class CacheAsidePattern {
    
    /**
     * 读取数据
     */
    public User getUser(Long userId) {
        // 1. 从缓存读取
        User user = cacheService.get("userInfo", String.valueOf(userId));
        
        if (user != null) {
            return user;  // 缓存命中
        }
        
        // 2. 缓存未命中,从数据库读取
        user = userRepository.findById(userId);
        
        if (user != null) {
            // 3. 写入缓存
            cacheService.put("userInfo", String.valueOf(userId), user);
        }
        
        return user;
    }
    
    /**
     * 更新数据
     */
    public void updateUser(User user) {
        // 1. 先更新数据库
        userRepository.update(user);
        
        // 2. 再删除缓存(而不是更新缓存)
        cacheService.evict("userInfo", String.valueOf(user.getId()));
        
        // 3. 发布缓存失效消息(分布式环境)
        cacheSyncService.publishEvictMessage("userInfo", String.valueOf(user.getId()));
    }
}
1.4.3 主动更新与被动失效
java 复制代码
/**
 * 主动更新:适用于数据变更可控的场景
 */
@EnhancedCachePut(
    cacheType = CacheType.USER_INFO,
    key = "#user.id",
    syncDistributed = true
)
public User updateUserActive(User user) {
    userRepository.update(user);
    return user;  // 返回值会写入缓存
}

/**
 * 被动失效:适用于数据变更不确定的场景
 */
@EnhancedCacheEvict(
    cacheType = CacheType.USER_INFO,
    key = "#userId",
    syncDistributed = true
)
public void updateUserPassive(Long userId, Map<String, Object> updates) {
    userRepository.updateFields(userId, updates);
    // 方法执行后自动删除缓存
}

/**
 * 定时刷新:适用于热点数据
 */
@Scheduled(fixedDelay = 300000) // 5分钟刷新一次
public void refreshHotData() {
    List<String> hotKeys = hotKeyDetector.getHotKeys(100);
    
    hotKeys.forEach(key -> {
        // 异步刷新
        CompletableFuture.runAsync(() -> {
            Object data = loadDataFromDB(key);
            cacheService.put("hotData", key, data);
        });
    });
}

2. 技术选型与依赖

2.1 核心技术栈

技术 版本 说明
Spring Boot 3.2.1 基础框架
Caffeine 3.1.8 本地缓存
Redis 7.0+ 分布式缓存
Redisson 3.25.2 分布式锁
Guava 32.1.3 布隆过滤器
Micrometer 1.12.1 监控指标
JDK 17+ 运行环境

2.2 Maven依赖配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
        <relativePath/>
    </parent>
    
    <groupId>com.enterprise</groupId>
    <artifactId>caffeine-cache-practice</artifactId>
    <version>1.0.0</version>
    <name>Enterprise Caffeine Cache Practice</name>
    <description>SpringBoot 3 + Caffeine 企业级缓存最佳实践</description>
    
    <properties>
        <java.version>17</java.version>
        <caffeine.version>3.1.8</caffeine.version>
        <redisson.version>3.25.2</redisson.version>
        <guava.version>32.1.3-jre</guava.version>
        <micrometer.version>1.12.1</micrometer.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring Boot Cache 抽象 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        
        <!-- Caffeine 本地缓存 -->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>${caffeine.version}</version>
        </dependency>
        
        <!-- Redis 分布式缓存 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <!-- Redisson 分布式锁 -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>${redisson.version}</version>
        </dependency>
        
        <!-- Spring Boot Actuator 监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <!-- Micrometer Prometheus -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>${micrometer.version}</version>
        </dependency>
        
        <!-- Guava 布隆过滤器 -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>
        
        <!-- AOP 切面编程 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        
        <!-- Lombok 代码简化 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- Configuration Processor -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- Test Dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3. 核心配置实现

3.1 项目结构

复制代码
caffeine-cache-practice/
├── src/main/java/com/enterprise/cache/
│   ├── CacheApplication.java                    # 启动类
│   │
│   ├── annotation/                               # 自定义注解
│   │   ├── EnhancedCacheable.java               # 增强缓存注解
│   │   ├── EnhancedCacheEvict.java              # 增强删除注解
│   │   └── EnhancedCachePut.java                # 增强更新注解
│   │
│   ├── aspect/                                   # AOP切面
│   │   └── EnhancedCacheAspect.java             # 缓存切面处理器
│   │
│   ├── config/                                   # 配置类
│   │   ├── CaffeineConfig.java                  # Caffeine配置
│   │   ├── RedisMessageConfig.java              # Redis消息配置
│   │   ├── AsyncExecutorConfig.java             # 异步执行器配置
│   │   └── properties/
│   │       └── CaffeineProperties.java          # 配置属性
│   │
│   ├── enums/                                    # 枚举类
│   │   └── CacheType.java                       # 缓存类型
│   │
│   ├── service/                                  # 服务层
│   │   ├── CacheService.java                    # 缓存服务接口
│   │   ├── BloomFilterService.java              # 布隆过滤器服务
│   │   ├── DistributedLockService.java          # 分布式锁服务
│   │   ├── CacheSyncService.java                # 缓存同步服务
│   │   └── impl/
│   │       └── CacheServiceImpl.java            # 缓存服务实现
│   │
│   ├── listener/                                 # 监听器
│   │   └── CacheRemovalListener.java            # 缓存移除监听器
│   │
│   ├── loader/                                   # 加载器
│   │   └── AsyncCacheLoader.java                # 异步缓存加载器
│   │
│   ├── model/                                    # 数据模型
│   │   └── CacheSyncMessage.java                # 缓存同步消息
│   │
│   ├── metrics/                                  # 监控指标
│   │   ├── CacheMetricsCollector.java           # 指标收集器
│   │   └── HotKeyDetector.java                  # 热点Key检测器
│   │
│   └── example/                                  # 示例代码
│       ├── entity/
│       ├── repository/
│       ├── service/
│       └── controller/
│
├── src/main/resources/
│   ├── application.yml                           # 主配置文件
│   ├── application-dev.yml                       # 开发环境
│   ├── application-test.yml                      # 测试环境
│   └── application-prod.yml                      # 生产环境
│
└── src/test/java/                                # 测试代码
    └── com/enterprise/cache/test/

3.2 主启动类

java 复制代码
package com.enterprise.cache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * 缓存应用启动类
 * 
 * 启用功能:
 * - @EnableCaching: Spring缓存抽象支持
 * - @EnableAsync: 异步方法支持
 * - @EnableScheduling: 定时任务支持
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@SpringBootApplication
@EnableCaching
@EnableAsync
@EnableScheduling
public class CacheApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}

3.3 Caffeine核心配置

java 复制代码
package com.enterprise.cache.config;

import com.enterprise.cache.config.properties.CaffeineProperties;
import com.enterprise.cache.enums.CacheType;
import com.enterprise.cache.listener.CacheRemovalListener;
import com.enterprise.cache.loader.AsyncCacheLoader;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Caffeine 缓存核心配置
 * 
 * 功能特性:
 * 1. 支持多缓存实例,每个实例独立配置
 * 2. 集成Prometheus监控指标
 * 3. 支持异步刷新机制
 * 4. 支持移除事件监听
 * 5. 支持自定义过期策略
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Configuration
@EnableCaching
@RequiredArgsConstructor
public class CaffeineConfig {
    
    private final CaffeineProperties caffeineProperties;
    private final MeterRegistry meterRegistry;
    private final CacheRemovalListener removalListener;
    private final AsyncCacheLoader asyncCacheLoader;
    
    /**
     * 主缓存管理器
     * 使用动态配置,支持多缓存实例
     * 
     * @return CacheManager实例
     */
    @Bean
    @Primary
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        
        // 设置所有缓存名称
        String[] cacheNames = Arrays.stream(CacheType.values())
                .map(CacheType::getCacheName)
                .toArray(String[]::new);
        cacheManager.setCacheNames(Arrays.asList(cacheNames));
        
        // 设置默认缓存配置
        cacheManager.setCaffeine(buildDefaultCaffeine());
        
        // 允许空值缓存(防止缓存穿透)
        cacheManager.setAllowNullValues(true);
        
        log.info("Caffeine CacheManager initialized with caches: {}", 
                Arrays.toString(cacheNames));
        return cacheManager;
    }
    
    /**
     * 构建默认 Caffeine 配置
     * 
     * @return Caffeine构建器
     */
    private Caffeine<Object, Object> buildDefaultCaffeine() {
        CaffeineProperties.CacheSpec spec = caffeineProperties.getDefaultSpec();
        
        Caffeine<Object, Object> builder = Caffeine.newBuilder()
                .initialCapacity(spec.getInitialCapacity())
                .maximumSize(spec.getMaximumSize())
                .expireAfterWrite(spec.getExpireAfterWrite(), TimeUnit.SECONDS)
                .expireAfterAccess(spec.getExpireAfterAccess(), TimeUnit.SECONDS);
        
        // 添加移除监听器
        builder.removalListener(removalListener);
        
        // 启用统计
        if (spec.getRecordStats()) {
            builder.recordStats();
        }
        
        // 弱引用/软引用配置(GC友好)
        if (spec.getWeakKeys()) {
            builder.weakKeys();
        }
        if (spec.getWeakValues()) {
            builder.weakValues();
        }
        if (spec.getSoftValues()) {
            builder.softValues();
        }
        
        return builder;
    }
    
    /**
     * 缓存实例Map
     * 提供直接访问各个缓存实例的能力
     * 
     * @return 缓存实例映射
     */
    @Bean
    public Map<String, Cache<String, Object>> cacheMap() {
        Map<String, Cache<String, Object>> map = new HashMap<>();
        
        map.put(CacheType.HOT_DATA.getCacheName(), hotDataCache());
        map.put(CacheType.NORMAL_DATA.getCacheName(), normalDataCache());
        map.put(CacheType.USER_INFO.getCacheName(), userInfoCache());
        map.put(CacheType.PRODUCT_INFO.getCacheName(), productInfoCache());
        map.put(CacheType.RATE_LIMIT.getCacheName(), rateLimitCache());
        
        return map;
    }
    
    /**
     * 热点数据缓存实例
     * 
     * 特点:
     * - 大容量:10,000条
     * - 短过期:100秒
     * - 高性能:适合秒杀、热榜等场景
     * 
     * @return Cache实例
     */
    @Bean("hotDataCache")
    public Cache<String, Object> hotDataCache() {
        Cache<String, Object> cache = Caffeine.newBuilder()
                .initialCapacity(200)
                .maximumSize(CacheType.HOT_DATA.getMaximumSize())
                .expireAfterWrite(CacheType.HOT_DATA.getExpireAfterWrite(), TimeUnit.SECONDS)
                .recordStats()
                .removalListener(removalListener)
                .build();
        
        // 注册到 Prometheus
        CaffeineCacheMetrics.monitor(meterRegistry, cache, 
                CacheType.HOT_DATA.getCacheName());
        
        log.info("Hot data cache initialized: maxSize={}, expireAfterWrite={}s",
                CacheType.HOT_DATA.getMaximumSize(),
                CacheType.HOT_DATA.getExpireAfterWrite());
        
        return cache;
    }
    
    /**
     * 常规数据缓存实例
     * 
     * 特点:
     * - 中等容量:5,000条
     * - 中等过期:5分钟写入过期,10分钟访问过期
     * - 通用场景:商品详情、用户信息等
     * 
     * @return Cache实例
     */
    @Bean("normalDataCache")
    public Cache<String, Object> normalDataCache() {
        Cache<String, Object> cache = Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(CacheType.NORMAL_DATA.getMaximumSize())
                .expireAfterWrite(CacheType.NORMAL_DATA.getExpireAfterWrite(), 
                        TimeUnit.SECONDS)
                .expireAfterAccess(CacheType.NORMAL_DATA.getExpireAfterAccess(), 
                        TimeUnit.SECONDS)
                .recordStats()
                .removalListener(removalListener)
                .build();
        
        CaffeineCacheMetrics.monitor(meterRegistry, cache, 
                CacheType.NORMAL_DATA.getCacheName());
        
        log.info("Normal data cache initialized");
        
        return cache;
    }
    
    /**
     * 用户信息缓存实例
     * 
     * @return Cache实例
     */
    @Bean("userInfoCache")
    public Cache<String, Object> userInfoCache() {
        Cache<String, Object> cache = Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(CacheType.USER_INFO.getMaximumSize())
                .expireAfterWrite(CacheType.USER_INFO.getExpireAfterWrite(), 
                        TimeUnit.SECONDS)
                .expireAfterAccess(CacheType.USER_INFO.getExpireAfterAccess(), 
                        TimeUnit.SECONDS)
                .recordStats()
                .removalListener(removalListener)
                .build();
        
        CaffeineCacheMetrics.monitor(meterRegistry, cache, 
                CacheType.USER_INFO.getCacheName());
        
        return cache;
    }
    
    /**
     * 商品信息缓存实例
     * 
     * @return Cache实例
     */
    @Bean("productInfoCache")
    public Cache<String, Object> productInfoCache() {
        Cache<String, Object> cache = Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(CacheType.PRODUCT_INFO.getMaximumSize())
                .expireAfterWrite(CacheType.PRODUCT_INFO.getExpireAfterWrite(), 
                        TimeUnit.SECONDS)
                .recordStats()
                .removalListener(removalListener)
                .build();
        
        CaffeineCacheMetrics.monitor(meterRegistry, cache, 
                CacheType.PRODUCT_INFO.getCacheName());
        
        return cache;
    }
    
    /**
     * 限流缓存实例
     * 
     * 特点:
     * - 极短过期:1秒
     * - 大容量:10,000条
     * - 无访问续期:expireAfterAccess未设置
     * 
     * @return Cache实例
     */
    @Bean("rateLimitCache")
    public Cache<String, Object> rateLimitCache() {
        Cache<String, Object> cache = Caffeine.newBuilder()
                .initialCapacity(1000)
                .maximumSize(CacheType.RATE_LIMIT.getMaximumSize())
                .expireAfterWrite(CacheType.RATE_LIMIT.getExpireAfterWrite(), 
                        TimeUnit.SECONDS)
                .recordStats()
                .build();
        
        CaffeineCacheMetrics.monitor(meterRegistry, cache, 
                CacheType.RATE_LIMIT.getCacheName());
        
        log.info("Rate limit cache initialized");
        
        return cache;
    }
    
    /**
     * 字典数据缓存实例(支持异步刷新)
     * 
     * 特点:
     * - 长过期:1小时
     * - 异步刷新:30分钟后自动刷新
     * - 适用场景:配置数据、枚举等静态数据
     * 
     * @return LoadingCache实例
     */
    @Bean("dictionaryCache")
    public LoadingCache<String, Object> dictionaryCache() {
        LoadingCache<String, Object> cache = Caffeine.newBuilder()
                .initialCapacity(50)
                .maximumSize(CacheType.DICTIONARY.getMaximumSize())
                .expireAfterWrite(CacheType.DICTIONARY.getExpireAfterWrite(), 
                        TimeUnit.SECONDS)
                .refreshAfterWrite(1800, TimeUnit.SECONDS) // 30分钟异步刷新
                .recordStats()
                .removalListener(removalListener)
                .buildAsync(asyncCacheLoader)
                .synchronous();
        
        CaffeineCacheMetrics.monitor(meterRegistry, cache, 
                CacheType.DICTIONARY.getCacheName());
        
        log.info("Dictionary cache initialized with async refresh");
        
        return cache;
    }
}

3.4 配置属性类

java 复制代码
package com.enterprise.cache.config.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * Caffeine 缓存配置属性
 * 
 * 支持:
 * 1. 多环境配置(dev/test/prod)
 * 2. 多实例独立配置
 * 3. 动态配置热更新
 * 4. 统计和预热配置
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Data
@Component
@ConfigurationProperties(prefix = "cache.caffeine")
public class CaffeineProperties {
    
    /**
     * 是否启用缓存
     */
    private boolean enabled = true;
    
    /**
     * 默认配置(所有缓存的基础配置)
     */
    private CacheSpec defaultSpec = new CacheSpec();
    
    /**
     * 各缓存实例的特定配置
     * Key: 缓存名称
     * Value: 缓存配置
     */
    private Map<String, CacheSpec> specs = new HashMap<>();
    
    /**
     * 统计配置
     */
    private StatsConfig stats = new StatsConfig();
    
    /**
     * 预热配置
     */
    private WarmUpConfig warmUp = new WarmUpConfig();
    
    /**
     * 缓存规格配置
     */
    @Data
    public static class CacheSpec {
        /**
         * 初始容量
         * 建议设置为 maximumSize 的 10-20%
         */
        private Integer initialCapacity = 100;
        
        /**
         * 最大容量
         * 超过此值会根据LRU策略驱逐
         */
        private Integer maximumSize = 1000;
        
        /**
         * 写入后过期时间(秒)
         * 从写入时开始计时
         */
        private Integer expireAfterWrite = 600;
        
        /**
         * 访问后过期时间(秒)
         * 从最后一次读或写开始计时
         */
        private Integer expireAfterAccess = 300;
        
        /**
         * 刷新时间(秒)
         * 用于异步刷新,需配合AsyncCacheLoader使用
         */
        private Integer refreshAfterWrite;
        
        /**
         * 是否开启统计
         * 开启后可以获取命中率、驱逐次数等指标
         */
        private Boolean recordStats = true;
        
        /**
         * 是否使用弱引用键
         * 适用于大量短生命周期的键
         */
        private Boolean weakKeys = false;
        
        /**
         * 是否使用弱引用值
         * GC时可能被回收
         */
        private Boolean weakValues = false;
        
        /**
         * 是否使用软引用值
         * 内存不足时会被GC回收
         */
        private Boolean softValues = false;
    }
    
    /**
     * 统计配置
     */
    @Data
    public static class StatsConfig {
        /**
         * 是否启用统计
         */
        private boolean enabled = true;
        
        /**
         * 统计输出间隔(秒)
         */
        private int outputInterval = 60;
        
        /**
         * 命中率阈值(低于此值告警)
         */
        private double hitRateThreshold = 0.7;
    }
    
    /**
     * 预热配置
     */
    @Data
    public static class WarmUpConfig {
        /**
         * 是否启用预热
         */
        private boolean enabled = false;
        
        /**
         * 预热延迟启动时间(秒)
         * 应用启动后延迟多久开始预热
         */
        private int delaySeconds = 30;
        
        /**
         * 预热数据批次大小
         */
        private int batchSize = 100;
    }
}

3.5 异步执行器配置

java 复制代码
package com.enterprise.cache.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异步任务执行器配置
 * 
 * 提供三个专用线程池:
 * 1. cacheRefreshExecutor - 缓存刷新
 * 2. cacheWarmUpExecutor - 缓存预热
 * 3. cacheStatsExecutor - 缓存统计
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncExecutorConfig {
    
    /**
     * 缓存刷新执行器
     * 
     * 配置说明:
     * - 核心线程数:4(根据CPU核心数调整)
     * - 最大线程数:8
     * - 队列容量:200
     * - 拒绝策略:CallerRunsPolicy(调用者运行)
     * 
     * @return Executor
     */
    @Bean("cacheRefreshExecutor")
    public Executor cacheRefreshExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(8);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("cache-refresh-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setKeepAliveSeconds(60);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(30);
        
        executor.initialize();
        return executor;
    }
    
    /**
     * 缓存预热执行器
     * 
     * 配置说明:
     * - 核心线程数:2
     * - 最大线程数:4
     * - 队列容量:50
     * 
     * @return Executor
     */
    @Bean("cacheWarmUpExecutor")
    public Executor cacheWarmUpExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(4);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("cache-warmup-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setKeepAliveSeconds(60);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(30);
        
        executor.initialize();
        return executor;
    }
    
    /**
     * 缓存统计执行器
     * 
     * 配置说明:
     * - 核心线程数:1
     * - 最大线程数:2
     * - 拒绝策略:DiscardOldestPolicy(丢弃最旧任务)
     * 
     * @return Executor
     */
    @Bean("cacheStatsExecutor")
    public Executor cacheStatsExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("cache-stats-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        executor.setKeepAliveSeconds(60);
        
        executor.initialize();
        return executor;
    }
}

二、核心功能实现与高级特性

1. 自定义缓存注解

1.1 增强型缓存注解定义

1.1.1 @EnhancedCacheable 注解
java 复制代码
package com.enterprise.cache.annotation;

import com.enterprise.cache.enums.CacheType;

import java.lang.annotation.*;

/**
 * 增强型缓存注解
 * 
 * 相比Spring @Cacheable的增强:
 * 1. 支持缓存穿透防护(空值缓存、布隆过滤器)
 * 2. 支持缓存击穿防护(互斥锁)
 * 3. 支持缓存雪崩防护(随机过期时间)
 * 4. 支持降级策略
 * 5. 支持异步刷新
 * 6. 支持监控打点
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnhancedCacheable {
    
    /**
     * 缓存名称
     * 对应CacheType中的cacheName
     */
    String cacheName();
    
    /**
     * 缓存类型
     * 默认为常规数据缓存
     */
    CacheType cacheType() default CacheType.NORMAL_DATA;
    
    /**
     * 缓存Key
     * 支持SpEL表达式,例如:#userId, #user.id
     */
    String key();
    
    /**
     * 缓存条件
     * 支持SpEL表达式,返回true时才缓存
     * 例如:#result != null
     */
    String condition() default "";
    
    /**
     * 排除缓存条件
     * 支持SpEL表达式,返回true时不缓存
     * 例如:#result.isEmpty()
     */
    String unless() default "";
    
    /**
     * 是否启用缓存穿透防护
     * 开启后会缓存null值,防止频繁查询不存在的数据
     */
    boolean preventPenetration() default true;
    
    /**
     * 空值缓存时间(秒)
     * 仅在preventPenetration=true时生效
     */
    int nullValueExpire() default 60;
    
    /**
     * 是否启用缓存击穿防护
     * 开启后使用互斥锁,同一时间只有一个线程加载数据
     */
    boolean preventBreakdown() default false;
    
    /**
     * 互斥锁等待超时时间(毫秒)
     */
    long lockTimeout() default 3000;
    
    /**
     * 是否启用缓存雪崩防护
     * 开启后会在过期时间上增加随机偏移
     */
    boolean preventAvalanche() default true;
    
    /**
     * 过期时间随机偏移范围(秒)
     * 例如设置300,实际过期时间会在 [0, 300] 秒内随机
     */
    int expireRandomRange() default 300;
    
    /**
     * 是否启用布隆过滤器
     * 适用于海量数据场景,快速判断key是否可能存在
     */
    boolean useBloomFilter() default false;
    
    /**
     * 降级返回值
     * 当缓存异常时返回的默认值(SpEL表达式)
     */
    String fallback() default "";
    
    /**
     * 是否异步刷新
     * 开启后过期数据先返回旧值,后台异步加载新值
     */
    boolean asyncRefresh() default false;
    
    /**
     * 是否记录监控指标
     */
    boolean recordMetrics() default true;
    
    /**
     * 缓存描述
     * 用于监控和日志
     */
    String description() default "";
}
1.1.2 @EnhancedCacheEvict 注解
java 复制代码
package com.enterprise.cache.annotation;

import java.lang.annotation.*;

/**
 * 增强型缓存失效注解
 * 
 * 增强功能:
 * 1. 支持批量删除(通配符匹配)
 * 2. 支持异步删除
 * 3. 支持分布式同步删除
 * 4. 支持条件删除
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnhancedCacheEvict {
    
    /**
     * 缓存名称
     */
    String cacheName();
    
    /**
     * 缓存Key
     * 支持SpEL表达式
     * 支持通配符(*)批量删除
     * 例如:user:* 删除所有user开头的缓存
     */
    String key() default "";
    
    /**
     * 是否删除所有缓存
     */
    boolean allEntries() default false;
    
    /**
     * 是否在方法执行前删除缓存
     * true: 方法执行前删除
     * false: 方法执行后删除(默认)
     */
    boolean beforeInvocation() default false;
    
    /**
     * 删除条件
     * 支持SpEL表达式
     */
    String condition() default "";
    
    /**
     * 是否异步删除
     * 适用于大量删除操作,避免阻塞业务线程
     */
    boolean async() default false;
    
    /**
     * 是否同步到其他节点
     * 通过Redis Pub/Sub通知其他节点删除本地缓存
     */
    boolean syncToCluster() default true;
}
1.1.3 @EnhancedCachePut 注解
java 复制代码
package com.enterprise.cache.annotation;

import com.enterprise.cache.enums.CacheType;

import java.lang.annotation.*;

/**
 * 增强型缓存更新注解
 * 
 * 增强功能:
 * 1. 支持条件更新
 * 2. 支持异步更新
 * 3. 支持分布式同步
 * 4. 支持TTL动态设置
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnhancedCachePut {
    
    /**
     * 缓存名称
     */
    String cacheName();
    
    /**
     * 缓存类型
     */
    CacheType cacheType() default CacheType.NORMAL_DATA;
    
    /**
     * 缓存Key
     * 支持SpEL表达式
     */
    String key();
    
    /**
     * 更新条件
     * 支持SpEL表达式
     */
    String condition() default "";
    
    /**
     * 排除条件
     */
    String unless() default "";
    
    /**
     * 是否异步更新
     */
    boolean async() default false;
    
    /**
     * 是否同步到其他节点
     */
    boolean syncToCluster() default true;
    
    /**
     * 自定义TTL(秒)
     * -1表示使用默认配置
     */
    int ttl() default -1;
}

1.2 缓存注解处理器

java 复制代码
package com.enterprise.cache.aspect;

import com.enterprise.cache.annotation.EnhancedCacheable;
import com.enterprise.cache.annotation.EnhancedCacheEvict;
import com.enterprise.cache.annotation.EnhancedCachePut;
import com.enterprise.cache.service.CacheService;
import com.enterprise.cache.util.SpelExpressionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 缓存注解切面处理器
 * 
 * 处理增强型缓存注解的核心逻辑:
 * 1. 缓存查询
 * 2. 缓存更新
 * 3. 缓存删除
 * 4. 防护机制
 * 5. 监控打点
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Aspect
@Order(1)
@Component
@RequiredArgsConstructor
public class CacheAspect {
    
    private final CacheService cacheService;
    private final SpelExpressionUtils spelUtils;
    
    /**
     * 互斥锁Map(用于缓存击穿防护)
     * Key: cacheName:cacheKey
     */
    private final java.util.concurrent.ConcurrentHashMap<String, Lock> lockMap = new java.util.concurrent.ConcurrentHashMap<>();
    
    /**
     * 处理 @EnhancedCacheable 注解
     */
    @Around("@annotation(enhancedCacheable)")
    public Object handleCacheable(ProceedingJoinPoint joinPoint, EnhancedCacheable enhancedCacheable) throws Throwable {
        
        // 1. 解析缓存Key
        String cacheKey = parseCacheKey(joinPoint, enhancedCacheable.key());
        String cacheName = enhancedCacheable.cacheName();
        String fullCacheKey = cacheName + ":" + cacheKey;
        
        log.debug("Cache query - cacheName: {}, key: {}", cacheName, cacheKey);
        
        // 2. 检查condition条件
        if (StringUtils.hasText(enhancedCacheable.condition())) {
            boolean condition = spelUtils.parseCondition(joinPoint, enhancedCacheable.condition());
            if (!condition) {
                log.debug("Condition not met, skip cache for key: {}", fullCacheKey);
                return joinPoint.proceed();
            }
        }
        
        // 3. 查询缓存
        Object cachedValue = cacheService.get(cacheName, cacheKey);
        
        if (cachedValue != null) {
            // 缓存命中
            log.debug("Cache hit for key: {}", fullCacheKey);
            
            // 记录监控指标
            if (enhancedCacheable.recordMetrics()) {
                recordCacheHit(cacheName);
            }
            
            return cachedValue;
        }
        
        // 4. 缓存未命中 - 判断是否需要防击穿
        if (enhancedCacheable.preventBreakdown()) {
            return handleCacheBreakdown(joinPoint, enhancedCacheable, fullCacheKey);
        }
        
        // 5. 执行目标方法
        Object result = joinPoint.proceed();
        
        // 6. 判断unless条件
        if (StringUtils.hasText(enhancedCacheable.unless())) {
            boolean unless = spelUtils.parseUnless(result, enhancedCacheable.unless());
            if (unless) {
                log.debug("Unless condition met, skip caching for key: {}", fullCacheKey);
                return result;
            }
        }
        
        // 7. 缓存结果
        cacheResult(enhancedCacheable, cacheName, cacheKey, result);
        
        // 8. 记录监控指标
        if (enhancedCacheable.recordMetrics()) {
            recordCacheMiss(cacheName);
        }
        
        return result;
    }
    
    /**
     * 处理缓存击穿(互斥锁机制)
     */
    private Object handleCacheBreakdown(ProceedingJoinPoint joinPoint, 
                                       EnhancedCacheable enhancedCacheable, 
                                       String fullCacheKey) throws Throwable {
        
        Lock lock = lockMap.computeIfAbsent(fullCacheKey, k -> new ReentrantLock());
        
        try {
            // 尝试获取锁
            boolean acquired = lock.tryLock(enhancedCacheable.lockTimeout(), TimeUnit.MILLISECONDS);
            
            if (!acquired) {
                log.warn("Failed to acquire lock for key: {}, timeout: {}ms", 
                        fullCacheKey, enhancedCacheable.lockTimeout());
                
                // 降级处理
                if (StringUtils.hasText(enhancedCacheable.fallback())) {
                    return spelUtils.parseFallback(enhancedCacheable.fallback());
                }
                
                // 直接查询数据库
                return joinPoint.proceed();
            }
            
            // 获取锁成功,再次检查缓存(双重检查)
            String cacheKey = parseCacheKey(joinPoint, enhancedCacheable.key());
            Object cachedValue = cacheService.get(enhancedCacheable.cacheName(), cacheKey);
            
            if (cachedValue != null) {
                return cachedValue;
            }
            
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 缓存结果
            cacheResult(enhancedCacheable, enhancedCacheable.cacheName(), cacheKey, result);
            
            return result;
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Lock interrupted for key: {}", fullCacheKey, e);
            throw e;
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 缓存结果
     */
    private void cacheResult(EnhancedCacheable enhancedCacheable, 
                            String cacheName, 
                            String cacheKey, 
                            Object result) {
        
        if (result == null && !enhancedCacheable.preventPenetration()) {
            // 不缓存null值
            return;
        }
        
        // 计算过期时间
        int expireTime = enhancedCacheable.cacheType().getExpireAfterWrite();
        
        // 防雪崩:添加随机偏移
        if (enhancedCacheable.preventAvalanche()) {
            int randomOffset = (int) (Math.random() * enhancedCacheable.expireRandomRange());
            expireTime += randomOffset;
        }
        
        // null值使用短过期时间
        if (result == null) {
            expireTime = enhancedCacheable.nullValueExpire();
        }
        
        // 异步刷新模式
        if (enhancedCacheable.asyncRefresh()) {
            cacheService.putAsync(cacheName, cacheKey, result, expireTime);
        } else {
            cacheService.put(cacheName, cacheKey, result, expireTime);
        }
        
        log.debug("Cached result for key: {}:{}, expire: {}s", cacheName, cacheKey, expireTime);
    }
    
    /**
     * 处理 @EnhancedCacheEvict 注解
     */
    @Around("@annotation(enhancedCacheEvict)")
    public Object handleCacheEvict(ProceedingJoinPoint joinPoint, EnhancedCacheEvict enhancedCacheEvict) throws Throwable {
        
        String cacheName = enhancedCacheEvict.cacheName();
        
        // beforeInvocation = true,方法执行前删除缓存
        if (enhancedCacheEvict.beforeInvocation()) {
            evictCache(joinPoint, enhancedCacheEvict);
        }
        
        Object result = joinPoint.proceed();
        
        // beforeInvocation = false,方法执行后删除缓存
        if (!enhancedCacheEvict.beforeInvocation()) {
            evictCache(joinPoint, enhancedCacheEvict);
        }
        
        return result;
    }
    
    /**
     * 删除缓存
     */
    private void evictCache(ProceedingJoinPoint joinPoint, EnhancedCacheEvict enhancedCacheEvict) {
        
        String cacheName = enhancedCacheEvict.cacheName();
        
        // 检查条件
        if (StringUtils.hasText(enhancedCacheEvict.condition())) {
            boolean condition = spelUtils.parseCondition(joinPoint, enhancedCacheEvict.condition());
            if (!condition) {
                log.debug("Evict condition not met for cache: {}", cacheName);
                return;
            }
        }
        
        // 删除所有缓存
        if (enhancedCacheEvict.allEntries()) {
            if (enhancedCacheEvict.async()) {
                cacheService.clearAsync(cacheName);
            } else {
                cacheService.clear(cacheName);
            }
            log.info("Evicted all entries in cache: {}", cacheName);
            return;
        }
        
        // 删除指定Key
        String cacheKey = parseCacheKey(joinPoint, enhancedCacheEvict.key());
        
        // 支持通配符删除
        if (cacheKey.contains("*")) {
            cacheService.evictPattern(cacheName, cacheKey);
            log.info("Evicted cache with pattern: {}:{}", cacheName, cacheKey);
        } else {
            if (enhancedCacheEvict.async()) {
                cacheService.evictAsync(cacheName, cacheKey);
            } else {
                cacheService.evict(cacheName, cacheKey);
            }
            log.debug("Evicted cache: {}:{}", cacheName, cacheKey);
        }
        
        // 同步到集群
        if (enhancedCacheEvict.syncToCluster()) {
            cacheService.publishEvictMessage(cacheName, cacheKey);
        }
    }
    
    /**
     * 处理 @EnhancedCachePut 注解
     */
    @Around("@annotation(enhancedCachePut)")
    public Object handleCachePut(ProceedingJoinPoint joinPoint, EnhancedCachePut enhancedCachePut) throws Throwable {
        
        Object result = joinPoint.proceed();
        
        // 检查条件
        if (StringUtils.hasText(enhancedCachePut.condition())) {
            boolean condition = spelUtils.parseCondition(joinPoint, enhancedCachePut.condition());
            if (!condition) {
                return result;
            }
        }
        
        // 检查unless
        if (StringUtils.hasText(enhancedCachePut.unless())) {
            boolean unless = spelUtils.parseUnless(result, enhancedCachePut.unless());
            if (unless) {
                return result;
            }
        }
        
        String cacheName = enhancedCachePut.cacheName();
        String cacheKey = parseCacheKey(joinPoint, enhancedCachePut.key());
        
        // 获取TTL
        int ttl = enhancedCachePut.ttl();
        if (ttl == -1) {
            ttl = enhancedCachePut.cacheType().getExpireAfterWrite();
        }
        
        // 更新缓存
        if (enhancedCachePut.async()) {
            cacheService.putAsync(cacheName, cacheKey, result, ttl);
        } else {
            cacheService.put(cacheName, cacheKey, result, ttl);
        }
        
        // 同步到集群
        if (enhancedCachePut.syncToCluster()) {
            cacheService.publishUpdateMessage(cacheName, cacheKey, result);
        }
        
        log.debug("Updated cache: {}:{}", cacheName, cacheKey);
        
        return result;
    }
    
    /**
     * 解析缓存Key(支持SpEL表达式)
     */
    private String parseCacheKey(ProceedingJoinPoint joinPoint, String keyExpression) {
        return spelUtils.parseKey(joinPoint, keyExpression);
    }
    
    /**
     * 记录缓存命中
     */
    private void recordCacheHit(String cacheName) {
        // 集成监控系统(Prometheus/Micrometer)
        // metricsService.recordCacheHit(cacheName);
    }
    
    /**
     * 记录缓存未命中
     */
    private void recordCacheMiss(String cacheName) {
        // 集成监控系统
        // metricsService.recordCacheMiss(cacheName);
    }
}

1.3 注解使用示例

java 复制代码
package com.enterprise.cache.example;

import com.enterprise.cache.annotation.EnhancedCacheable;
import com.enterprise.cache.annotation.EnhancedCacheEvict;
import com.enterprise.cache.annotation.EnhancedCachePut;
import com.enterprise.cache.enums.CacheType;
import org.springframework.stereotype.Service;

/**
 * 缓存注解使用示例
 */
@Service
public class UserCacheExample {
    
    /**
     * 示例1: 基础查询缓存
     * - 缓存用户信息
     * - 启用穿透防护(null值缓存)
     * - 启用雪崩防护(随机过期时间)
     */
    @EnhancedCacheable(
        cacheName = "userInfo",
        cacheType = CacheType.USER_INFO,
        key = "'user:' + #userId",
        preventPenetration = true,
        preventAvalanche = true,
        description = "用户基础信息缓存"
    )
    public UserDTO getUserById(Long userId) {
        // 从数据库查询
        return userRepository.findById(userId).orElse(null);
    }
    
    /**
     * 示例2: 条件缓存
     * - 只有当用户状态为ACTIVE时才缓存
     */
    @EnhancedCacheable(
        cacheName = "userInfo",
        cacheType = CacheType.USER_INFO,
        key = "'user:' + #userId",
        condition = "#userId > 0",
        unless = "#result == null || #result.status != 'ACTIVE'"
    )
    public UserDTO getActiveUser(Long userId) {
        return userRepository.findActiveUser(userId);
    }
    
    /**
     * 示例3: 缓存击穿防护
     * - 热点数据使用互斥锁
     * - 同一时间只有一个线程加载
     */
    @EnhancedCacheable(
        cacheName = "hotData",
        cacheType = CacheType.HOT_DATA,
        key = "'product:' + #productId",
        preventBreakdown = true,
        lockTimeout = 5000,
        fallback = "getDefaultProduct()"
    )
    public ProductDTO getHotProduct(Long productId) {
        return productRepository.findById(productId).orElse(null);
    }
    
    /**
     * 示例4: 异步刷新
     * - 过期后先返回旧值
     * - 后台异步加载新值
     */
    @EnhancedCacheable(
        cacheName = "normalData",
        cacheType = CacheType.NORMAL_DATA,
        key = "'article:' + #articleId",
        asyncRefresh = true
    )
    public ArticleDTO getArticle(Long articleId) {
        return articleRepository.findById(articleId).orElse(null);
    }
    
    /**
     * 示例5: 更新缓存
     * - 更新数据后同步更新缓存
     */
    @EnhancedCachePut(
        cacheName = "userInfo",
        cacheType = CacheType.USER_INFO,
        key = "'user:' + #user.id",
        syncToCluster = true
    )
    public UserDTO updateUser(UserDTO user) {
        return userRepository.save(user);
    }
    
    /**
     * 示例6: 删除缓存
     * - 删除用户后清除缓存
     */
    @EnhancedCacheEvict(
        cacheName = "userInfo",
        key = "'user:' + #userId",
        syncToCluster = true
    )
    public void deleteUser(Long userId) {
        userRepository.deleteById(userId);
    }
    
    /**
     * 示例7: 批量删除缓存
     * - 使用通配符删除所有用户缓存
     */
    @EnhancedCacheEvict(
        cacheName = "userInfo",
        key = "'user:*'",
        async = true,
        syncToCluster = true
    )
    public void clearAllUserCache() {
        // 业务逻辑
    }
    
    /**
     * 示例8: 条件删除
     * - 只有当删除成功时才清除缓存
     */
    @EnhancedCacheEvict(
        cacheName = "userInfo",
        key = "'user:' + #userId",
        condition = "#result == true"
    )
    public boolean softDeleteUser(Long userId) {
        return userRepository.softDelete(userId);
    }
}

2. 缓存Key生成策略

2.1 Key生成器接口

java 复制代码
package com.enterprise.cache.key;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 缓存Key生成器接口
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
public interface CacheKeyGenerator {
    
    /**
     * 生成缓存Key
     *
     * @param joinPoint 切点
     * @param keyExpression SpEL表达式
     * @return 缓存Key
     */
    String generateKey(ProceedingJoinPoint joinPoint, String keyExpression);
    
    /**
     * 根据参数生成Key
     *
     * @param prefix 前缀
     * @param params 参数
     * @return 缓存Key
     */
    String generateKey(String prefix, Object... params);
}

2.2 默认Key生成器实现

java 复制代码
package com.enterprise.cache.key.impl;

import com.enterprise.cache.key.CacheKeyGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * 默认缓存Key生成器
 * 
 * 策略:
 * 1. 简单类型:直接toString
 * 2. 对象类型:JSON序列化后MD5
 * 3. 集合类型:拼接后MD5
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component("defaultKeyGenerator")
@RequiredArgsConstructor
public class DefaultCacheKeyGenerator implements CacheKeyGenerator {
    
    private final ObjectMapper objectMapper;
    
    @Override
    public String generateKey(ProceedingJoinPoint joinPoint, String keyExpression) {
        // 这里简化处理,实际会使用SpEL解析
        Object[] args = joinPoint.getArgs();
        return generateKey("", args);
    }
    
    @Override
    public String generateKey(String prefix, Object... params) {
        if (params == null || params.length == 0) {
            return prefix;
        }
        
        StringBuilder keyBuilder = new StringBuilder();
        
        if (StringUtils.hasText(prefix)) {
            keyBuilder.append(prefix).append(":");
        }
        
        for (Object param : params) {
            if (param == null) {
                keyBuilder.append("null");
            } else if (isSimpleType(param)) {
                keyBuilder.append(param);
            } else {
                // 复杂对象序列化为JSON再MD5
                String json = toJson(param);
                String md5 = md5(json);
                keyBuilder.append(md5);
            }
            keyBuilder.append(":");
        }
        
        // 移除最后一个冒号
        if (keyBuilder.length() > 0 && keyBuilder.charAt(keyBuilder.length() - 1) == ':') {
            keyBuilder.setLength(keyBuilder.length() - 1);
        }
        
        return keyBuilder.toString();
    }
    
    /**
     * 判断是否为简单类型
     */
    private boolean isSimpleType(Object obj) {
        return obj instanceof String
                || obj instanceof Number
                || obj instanceof Boolean
                || obj instanceof Character
                || obj.getClass().isPrimitive();
    }
    
    /**
     * 对象转JSON
     */
    private String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize object to JSON: {}", obj, e);
            return obj.toString();
        }
    }
    
    /**
     * MD5加密
     */
    private String md5(String text) {
        return DigestUtils.md5DigestAsHex(text.getBytes(StandardCharsets.UTF_8));
    }
}

2.3 自定义Key生成器

java 复制代码
package com.enterprise.cache.key.impl;

import com.enterprise.cache.key.CacheKeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * 业务自定义Key生成器
 * 
 * 示例:根据业务规则生成Key
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component("businessKeyGenerator")
public class BusinessCacheKeyGenerator implements CacheKeyGenerator {
    
    @Override
    public String generateKey(ProceedingJoinPoint joinPoint, String keyExpression) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        // 业务规则:方法名 + 参数拼接
        String params = Arrays.stream(args)
                .map(Object::toString)
                .collect(Collectors.joining("_"));
        
        return methodName + ":" + params;
    }
    
    @Override
    public String generateKey(String prefix, Object... params) {
        return prefix + ":" + Arrays.stream(params)
                .map(Object::toString)
                .collect(Collectors.joining("_"));
    }
}

3. 缓存加载器实现

3.1 异步缓存加载器

java 复制代码
package com.enterprise.cache.loader;

import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;

/**
 * 异步缓存加载器
 * 
 * 功能:
 * 1. 支持异步加载数据
 * 2. 过期后先返回旧值,后台刷新
 * 3. 避免缓存击穿
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
public class AsyncCaffeineCacheLoader implements AsyncCacheLoader<String, Object> {
    
    private final Executor executor;
    private final Function<String, Object> dataLoader;
    
    public AsyncCaffeineCacheLoader(Executor cacheRefreshExecutor, 
                                   Function<String, Object> dataLoader) {
        this.executor = cacheRefreshExecutor;
        this.dataLoader = dataLoader;
    }
    
    @Override
    public @NonNull CompletableFuture<Object> asyncLoad(@NonNull String key, 
                                                        @NonNull Executor executor) {
        
        return CompletableFuture.supplyAsync(() -> {
            log.debug("Async loading cache for key: {}", key);
            
            try {
                Object value = dataLoader.apply(key);
                
                if (value == null) {
                    log.warn("Loaded null value for key: {}", key);
                }
                
                return value;
                
            } catch (Exception e) {
                log.error("Failed to load cache for key: {}", key, e);
                throw new RuntimeException("Cache load failed: " + key, e);
            }
        }, this.executor);
    }
    
    @Override
    public @NonNull CompletableFuture<Object> asyncReload(
            @NonNull String key, 
            @NonNull Object oldValue, 
            @NonNull Executor executor) {
        
        return CompletableFuture.supplyAsync(() -> {
            log.debug("Async reloading cache for key: {}, old value exists: {}", 
                    key, oldValue != null);
            
            try {
                Object newValue = dataLoader.apply(key);
                
                if (newValue == null) {
                    log.warn("Reloaded null value for key: {}, returning old value", key);
                    return oldValue; // 返回旧值
                }
                
                return newValue;
                
            } catch (Exception e) {
                log.error("Failed to reload cache for key: {}, returning old value", key, e);
                return oldValue; // 降级返回旧值
            }
        }, this.executor);
    }
}

3.2 批量缓存加载器

java 复制代码
package com.enterprise.cache.loader;

import com.github.benmanes.caffeine.cache.CacheLoader;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 批量缓存加载器
 * 
 * 功能:
 * 1. 支持批量加载
 * 2. 减少数据库查询次数
 * 3. 提高加载效率
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class BatchCaffeineCacheLoader implements CacheLoader<String, Object> {
    
    private final Function<Set<String>, Map<String, Object>> batchLoader;
    
    @Override
    public @Nullable Object load(@NonNull String key) {
        log.debug("Loading single cache for key: {}", key);
        
        try {
            Map<String, Object> result = batchLoader.apply(Set.of(key));
            return result.get(key);
        } catch (Exception e) {
            log.error("Failed to load cache for key: {}", key, e);
            return null;
        }
    }
    
    @Override
    public @NonNull Map<String, Object> loadAll(@NonNull Set<? extends String> keys) {
        log.debug("Batch loading cache for {} keys", keys.size());
        
        try {
            Set<String> keySet = keys.stream()
                    .map(Object::toString)
                    .collect(Collectors.toSet());
            
            Map<String, Object> result = batchLoader.apply(keySet);
            
            log.debug("Batch loaded {} entries", result.size());
            
            return result != null ? result : new HashMap<>();
            
        } catch (Exception e) {
            log.error("Failed to batch load cache for keys: {}", keys, e);
            return new HashMap<>();
        }
    }
}

4. 缓存防护机制

4.1 缓存穿透防护

4.1.1 布隆过滤器实现
java 复制代码
package com.enterprise.cache.guard;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 布隆过滤器管理器
 * 
 * 用于缓存穿透防护:
 * 1. 快速判断Key是否可能存在
 * 2. 避免查询不存在的数据
 * 3. 支持多个过滤器实例
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
public class BloomFilterManager {
    
    /**
     * 布隆过滤器容器
     * Key: 过滤器名称
     * Value: 布隆过滤器实例
     */
    private final ConcurrentHashMap<String, BloomFilter<String>> bloomFilters = new ConcurrentHashMap<>();
    
    /**
     * 默认预期插入数量
     */
    private static final int DEFAULT_EXPECTED_INSERTIONS = 1_000_000;
    
    /**
     * 默认误判率
     */
    private static final double DEFAULT_FPP = 0.01;
    
    @PostConstruct
    public void init() {
        log.info("BloomFilter manager initialized");
    }
    
    /**
     * 创建布隆过滤器
     *
     * @param name 过滤器名称
     * @param expectedInsertions 预期插入数量
     * @param fpp 误判率 (False Positive Probability)
     */
    public void createBloomFilter(String name, int expectedInsertions, double fpp) {
        BloomFilter<String> filter = BloomFilter.create(
                Funnels.stringFunnel(StandardCharsets.UTF_8),
                expectedInsertions,
                fpp
        );
        
        bloomFilters.put(name, filter);
        log.info("Created BloomFilter: {}, expectedInsertions: {}, fpp: {}", 
                name, expectedInsertions, fpp);
    }
    
    /**
     * 创建默认配置的布隆过滤器
     */
    public void createBloomFilter(String name) {
        createBloomFilter(name, DEFAULT_EXPECTED_INSERTIONS, DEFAULT_FPP);
    }
    
    /**
     * 添加元素到布隆过滤器
     */
    public void put(String filterName, String key) {
        BloomFilter<String> filter = bloomFilters.get(filterName);
        if (filter == null) {
            log.warn("BloomFilter not found: {}, creating default", filterName);
            createBloomFilter(filterName);
            filter = bloomFilters.get(filterName);
        }
        
        filter.put(key);
    }
    
    /**
     * 批量添加元素
     */
    public void putAll(String filterName, Iterable<String> keys) {
        keys.forEach(key -> put(filterName, key));
    }
    
    /**
     * 判断元素是否可能存在
     *
     * @return true: 可能存在, false: 一定不存在
     */
    public boolean mightContain(String filterName, String key) {
        BloomFilter<String> filter = bloomFilters.get(filterName);
        if (filter == null) {
            log.warn("BloomFilter not found: {}, returning false", filterName);
            return false;
        }
        
        return filter.mightContain(key);
    }
    
    /**
     * 获取过滤器
     */
    public BloomFilter<String> getFilter(String name) {
        return bloomFilters.get(name);
    }
    
    /**
     * 移除过滤器
     */
    public void removeFilter(String name) {
        bloomFilters.remove(name);
        log.info("Removed BloomFilter: {}", name);
    }
}
4.1.2 空值缓存策略
java 复制代码
package com.enterprise.cache.guard;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Optional;

/**
 * 缓存穿透防护器
 * 
 * 策略:
 * 1. 布隆过滤器前置判断
 * 2. 空值缓存(短TTL)
 * 3. 限流降级
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CachePenetrationGuard {
    
    private final BloomFilterManager bloomFilterManager;
    
    /**
     * 空值缓存标记
     */
    public static final String NULL_VALUE = "@@NULL@@";
    
    /**
     * 空值缓存过期时间(秒)
     */
    private static final int NULL_VALUE_EXPIRE = 60;
    
    /**
     * 检查Key是否可能存在
     *
     * @param filterName 过滤器名称
     * @param key 缓存Key
     * @return true: 可能存在, false: 一定不存在
     */
    public boolean mightExist(String filterName, String key) {
        boolean exists = bloomFilterManager.mightContain(filterName, key);
        
        if (!exists) {
            log.debug("Key definitely not exists (BloomFilter): {}", key);
        }
        
        return exists;
    }
    
    /**
     * 注册存在的Key到布隆过滤器
     */
    public void registerKey(String filterName, String key) {
        bloomFilterManager.put(filterName, key);
        log.debug("Registered key to BloomFilter: {}", key);
    }
    
    /**
     * 批量注册Key
     */
    public void registerKeys(String filterName, Iterable<String> keys) {
        bloomFilterManager.putAll(filterName, keys);
        log.info("Batch registered keys to BloomFilter");
    }
    
    /**
     * 判断是否为空值缓存
     */
    public boolean isNullValue(Object value) {
        return NULL_VALUE.equals(value);
    }
    
    /**
     * 获取空值缓存过期时间
     */
    public int getNullValueExpire() {
        return NULL_VALUE_EXPIRE;
    }
    
    /**
     * 包装可能为null的值
     */
    public Object wrapNullValue(Object value) {
        return value != null ? value : NULL_VALUE;
    }
    
    /**
     * 解包可能的空值
     */
    public Optional<Object> unwrapNullValue(Object value) {
        if (isNullValue(value)) {
            return Optional.empty();
        }
        return Optional.ofNullable(value);
    }
}

4.2 缓存击穿防护

java 复制代码
package com.enterprise.cache.guard;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

/**
 * 缓存击穿防护器
 * 
 * 策略:
 * 1. 互斥锁机制(同一Key只有一个线程加载)
 * 2. 永不过期(逻辑过期 + 异步刷新)
 * 3. 提前刷新
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
public class CacheBreakdownGuard {
    
    /**
     * 互斥锁容器
     * Key: cacheKey
     * Value: Lock
     */
    private final ConcurrentHashMap<String, Lock> lockMap = new ConcurrentHashMap<>();
    
    /**
     * 默认锁超时时间(秒)
     */
    private static final long DEFAULT_LOCK_TIMEOUT = 3;
    
    /**
     * 使用互斥锁保护缓存加载
     *
     * @param key 缓存Key
     * @param loader 数据加载器
     * @param timeout 锁超时时间(秒)
     * @return 加载的数据
     */
    public <T> T loadWithLock(String key, Supplier<T> loader, long timeout) {
        Lock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
        
        try {
            // 尝试获取锁
            boolean acquired = lock.tryLock(timeout, TimeUnit.SECONDS);
            
            if (!acquired) {
                log.warn("Failed to acquire lock for key: {}, timeout: {}s", key, timeout);
                // 超时返回null或抛出异常
                return null;
            }
            
            try {
                log.debug("Lock acquired for key: {}", key);
                return loader.get();
            } finally {
                lock.unlock();
                log.debug("Lock released for key: {}", key);
            }
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Lock interrupted for key: {}", key, e);
            return null;
        } finally {
            // 清理锁(可选:避免内存泄漏)
            lockMap.remove(key);
        }
    }
    
    /**
     * 使用默认超时时间
     */
    public <T> T loadWithLock(String key, Supplier<T> loader) {
        return loadWithLock(key, loader, DEFAULT_LOCK_TIMEOUT);
    }
    
    /**
     * 获取锁数量(监控用)
     */
    public int getLockCount() {
        return lockMap.size();
    }
    
    /**
     * 清理所有锁
     */
    public void clearLocks() {
        lockMap.clear();
        log.info("All locks cleared");
    }
}

4.3 缓存雪崩防护

java 复制代码
package com.enterprise.cache.guard;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 缓存雪崩防护器
 * 
 * 策略:
 * 1. 过期时间随机化
 * 2. 缓存预热
 * 3. 限流降级
 * 4. 多级缓存
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
public class CacheAvalancheGuard {
    
    /**
     * 默认随机偏移范围(秒)
     */
    private static final int DEFAULT_RANDOM_RANGE = 300;
    
    /**
     * 添加随机过期时间偏移
     *
     * @param baseExpire 基础过期时间(秒)
     * @param randomRange 随机范围(秒)
     * @return 添加偏移后的过期时间
     */
    public int addRandomExpire(int baseExpire, int randomRange) {
        int randomOffset = ThreadLocalRandom.current().nextInt(randomRange);
        int finalExpire = baseExpire + randomOffset;
        
        log.debug("Base expire: {}s, random offset: {}s, final expire: {}s", 
                baseExpire, randomOffset, finalExpire);
        
        return finalExpire;
    }
    
    /**
     * 使用默认随机范围
     */
    public int addRandomExpire(int baseExpire) {
        return addRandomExpire(baseExpire, DEFAULT_RANDOM_RANGE);
    }
    
    /**
     * 获取随机过期时间(百分比偏移)
     *
     * @param baseExpire 基础过期时间
     * @param percentRange 百分比范围 (0-100)
     * @return 随机过期时间
     */
    public int getRandomExpireByPercent(int baseExpire, int percentRange) {
        if (percentRange < 0 || percentRange > 100) {
            throw new IllegalArgumentException("percentRange must be between 0 and 100");
        }
        
        int maxOffset = (int) (baseExpire * percentRange / 100.0);
        int randomOffset = ThreadLocalRandom.current().nextInt(maxOffset);
        
        return baseExpire + randomOffset;
    }
    
    /**
     * 错峰过期策略
     * 将缓存过期时间分散到不同时间段
     *
     * @param baseExpire 基础过期时间
     * @param bucketCount 时间桶数量
     * @param bucketIndex 当前桶索引
     * @return 错峰后的过期时间
     */
    public int getStaggeredExpire(int baseExpire, int bucketCount, int bucketIndex) {
        if (bucketCount <= 0 || bucketIndex < 0 || bucketIndex >= bucketCount) {
            throw new IllegalArgumentException("Invalid bucket parameters");
        }
        
        int bucketSize = baseExpire / bucketCount;
        int offset = bucketSize * bucketIndex;
        
        log.debug("Staggered expire - base: {}s, bucket: {}/{}, offset: {}s", 
                baseExpire, bucketIndex, bucketCount, offset);
        
        return baseExpire + offset;
    }
}

5. 分布式缓存一致性

5.1 缓存同步管理器

java 复制代码
package com.enterprise.cache.sync;

import com.enterprise.cache.event.CacheEvictEvent;
import com.enterprise.cache.event.CacheUpdateEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.stereotype.Component;

/**
 * 缓存同步管理器
 * 
 * 基于Redis Pub/Sub实现分布式缓存一致性:
 * 1. 发布缓存失效消息
 * 2. 发布缓存更新消息
 * 3. 订阅并处理其他节点的消息
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheSyncManager {
    
    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper;
    
    /**
     * 缓存失效Topic
     */
    public static final String CACHE_EVICT_TOPIC = "cache:evict";
    
    /**
     * 缓存更新Topic
     */
    public static final String CACHE_UPDATE_TOPIC = "cache:update";
    
    /**
     * 发布缓存失效消息
     *
     * @param cacheName 缓存名称
     * @param cacheKey 缓存Key
     */
    public void publishEvictMessage(String cacheName, String cacheKey) {
        try {
            CacheEvictEvent event = new CacheEvictEvent(cacheName, cacheKey);
            String message = objectMapper.writeValueAsString(event);
            
            redisTemplate.convertAndSend(CACHE_EVICT_TOPIC, message);
            
            log.debug("Published cache evict message: {}:{}", cacheName, cacheKey);
            
        } catch (Exception e) {
            log.error("Failed to publish evict message: {}:{}", cacheName, cacheKey, e);
        }
    }
    
    /**
     * 发布缓存更新消息
     *
     * @param cacheName 缓存名称
     * @param cacheKey 缓存Key
     * @param value 更新的值
     */
    public void publishUpdateMessage(String cacheName, String cacheKey, Object value) {
        try {
            CacheUpdateEvent event = new CacheUpdateEvent(cacheName, cacheKey, value);
            String message = objectMapper.writeValueAsString(event);
            
            redisTemplate.convertAndSend(CACHE_UPDATE_TOPIC, message);
            
            log.debug("Published cache update message: {}:{}", cacheName, cacheKey);
            
        } catch (Exception e) {
            log.error("Failed to publish update message: {}:{}", cacheName, cacheKey, e);
        }
    }
    
    /**
     * 发布批量失效消息
     */
    public void publishBatchEvictMessage(String cacheName, Iterable<String> keys) {
        keys.forEach(key -> publishEvictMessage(cacheName, key));
    }
    
    /**
     * 发布清空缓存消息
     */
    public void publishClearMessage(String cacheName) {
        publishEvictMessage(cacheName, "*");
    }
}

5.2 缓存失效通知

java 复制代码
package com.enterprise.cache.sync;

import com.enterprise.cache.event.CacheEvictEvent;
import com.enterprise.cache.event.CacheUpdateEvent;
import com.enterprise.cache.service.CacheService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;

/**
 * 缓存同步消息监听器
 * 
 * 监听其他节点发送的缓存失效/更新消息
 * 并同步更新本地缓存
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheSyncListener implements MessageListener {
    
    private final CacheService cacheService;
    private final ObjectMapper objectMapper;
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        try {
            String channel = new String(message.getChannel());
            String body = new String(message.getBody());
            
            log.debug("Received cache sync message from channel: {}", channel);
            
            if (CacheSyncManager.CACHE_EVICT_TOPIC.equals(channel)) {
                handleEvictMessage(body);
            } else if (CacheSyncManager.CACHE_UPDATE_TOPIC.equals(channel)) {
                handleUpdateMessage(body);
            }
            
        } catch (Exception e) {
            log.error("Failed to handle cache sync message", e);
        }
    }
    
    /**
     * 处理缓存失效消息
     */
    private void handleEvictMessage(String messageBody) {
        try {
            CacheEvictEvent event = objectMapper.readValue(messageBody, CacheEvictEvent.class);
            
            String cacheName = event.getCacheName();
            String cacheKey = event.getCacheKey();
            
            if ("*".equals(cacheKey)) {
                // 清空整个缓存
                cacheService.clearLocal(cacheName);
                log.info("Cleared local cache: {}", cacheName);
            } else {
                // 删除指定Key
                cacheService.evictLocal(cacheName, cacheKey);
                log.debug("Evicted local cache: {}:{}", cacheName, cacheKey);
            }
            
        } catch (Exception e) {
            log.error("Failed to handle evict message: {}", messageBody, e);
        }
    }
    
    /**
     * 处理缓存更新消息
     */
    private void handleUpdateMessage(String messageBody) {
        try {
            CacheUpdateEvent event = objectMapper.readValue(messageBody, CacheUpdateEvent.class);
            
            String cacheName = event.getCacheName();
            String cacheKey = event.getCacheKey();
            Object value = event.getValue();
            
            // 更新本地缓存
            cacheService.putLocal(cacheName, cacheKey, value);
            
            log.debug("Updated local cache: {}:{}", cacheName, cacheKey);
            
        } catch (Exception e) {
            log.error("Failed to handle update message: {}", messageBody, e);
        }
    }
}

6. 缓存预热机制

6.1 预热数据加载器

java 复制代码
package com.enterprise.cache.warmup;

import com.enterprise.cache.service.CacheService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * 缓存预热数据加载器
 * 
 * 功能:
 * 1. 应用启动时预加载热点数据
 * 2. 批量加载数据
 * 3. 异步预热,不阻塞启动
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheWarmUpLoader {
    
    private final CacheService cacheService;
    
    /**
     * 预热用户信息缓存
     */
    public void warmUpUserCache(List<Long> userIds) {
        log.info("Starting to warm up user cache, count: {}", userIds.size());
        
        long startTime = System.currentTimeMillis();
        int successCount = 0;
        
        for (Long userId : userIds) {
            try {
                // 从数据库加载用户信息
                // UserDTO user = userService.loadUser(userId);
                
                // 放入缓存
                // cacheService.put("userInfo", "user:" + userId, user);
                
                successCount++;
                
            } catch (Exception e) {
                log.error("Failed to warm up user cache: {}", userId, e);
            }
        }
        
        long costTime = System.currentTimeMillis() - startTime;
        log.info("User cache warm-up completed, success: {}/{}, cost: {}ms", 
                successCount, userIds.size(), costTime);
    }
    
    /**
     * 批量预热缓存(通用方法)
     *
     * @param cacheName 缓存名称
     * @param dataMap Key-Value映射
     */
    public void batchWarmUp(String cacheName, Map<String, Object> dataMap) {
        log.info("Batch warming up cache: {}, size: {}", cacheName, dataMap.size());
        
        long startTime = System.currentTimeMillis();
        int successCount = 0;
        
        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
            try {
                cacheService.put(cacheName, entry.getKey(), entry.getValue());
                successCount++;
            } catch (Exception e) {
                log.error("Failed to warm up cache: {}:{}", cacheName, entry.getKey(), e);
            }
        }
        
        long costTime = System.currentTimeMillis() - startTime;
        log.info("Cache warm-up completed for {}, success: {}/{}, cost: {}ms", 
                cacheName, successCount, dataMap.size(), costTime);
    }
    
    /**
     * 预热字典数据
     */
    public void warmUpDictionaryCache() {
        log.info("Starting to warm up dictionary cache");
        
        // 从数据库加载字典数据
        // Map<String, DictDTO> dictMap = dictService.loadAllDictionaries();
        
        // 批量预热
        // batchWarmUp("dictionary", dictMap);
    }
    
    /**
     * 预热热点商品
     */
    public void warmUpHotProducts(List<Long> productIds) {
        log.info("Starting to warm up hot products, count: {}", productIds.size());
        
        // 类似用户缓存预热逻辑
    }
}

6.2 预热任务调度

java 复制代码
package com.enterprise.cache.warmup;

import com.enterprise.cache.config.properties.CaffeineProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 缓存预热任务调度器
 * 
 * 功能:
 * 1. 应用启动时自动预热
 * 2. 定时刷新热点数据
 * 3. 异步执行,不阻塞主流程
 * 
 * @author Enterprise Team
 * @version 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheWarmUpScheduler {
    
    private final CacheWarmUpLoader warmUpLoader;
    private final CaffeineProperties caffeineProperties;
    
    /**
     * 应用启动后执行预热
     */
    @Async("cacheWarmUpExecutor")
    @EventListener(ApplicationReadyEvent.class)
    public void onApplicationReady() {
        if (!caffeineProperties.getWarmUp().isEnabled()) {
            log.info("Cache warm-up is disabled");
            return;
        }
        
        int delaySeconds = caffeineProperties.getWarmUp().getDelaySeconds();
        
        log.info("Cache warm-up will start after {} seconds", delaySeconds);
        
        try {
            Thread.sleep(delaySeconds * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Warm-up delay interrupted", e);
            return;
        }
        
        executeWarmUp();
    }
    
    /**
     * 执行预热任务
     */
    private void executeWarmUp() {
        log.info("=== Starting cache warm-up ===");
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 预热字典数据
            warmUpLoader.warmUpDictionaryCache();
            
            // 2. 预热热点用户
            // List<Long> hotUserIds = getHotUserIds();
            // warmUpLoader.warmUpUserCache(hotUserIds);
            
            // 3. 预热热点商品
            // List<Long> hotProductIds = getHotProductIds();
            // warmUpLoader.warmUpHotProducts(hotProductIds);
            
        } catch (Exception e) {
            log.error("Cache warm-up failed", e);
        }
        
        long costTime = System.currentTimeMillis() - startTime;
        log.info("=== Cache warm-up completed, total cost: {}ms ===", costTime);
    }
    
    /**
     * 定时刷新热点数据
     * 每小时执行一次
     */
    @Async("cacheWarmUpExecutor")
    @Scheduled(cron = "0 0 * * * ?")
    public void scheduledRefreshHotData() {
        log.info("Starting scheduled hot data refresh");
        
        // 刷新热点商品
        // warmUpLoader.warmUpHotProducts(getHotProductIds());
    }
    
    /**
     * 获取热点用户ID列表
     * 可以从统计系统或推荐系统获取
     */
    private List<Long> getHotUserIds() {
        // TODO: 实现获取热点用户逻辑
        return List.of();
    }
    
    /**
     * 获取热点商品ID列表
     */
    private List<Long> getHotProductIds() {
        // TODO: 实现获取热点商品逻辑
        return List.of();
    }
}

相关推荐
青云计划17 小时前
知光项目知文发布模块
java·后端·spring·mybatis
Victor35617 小时前
MongoDB(9)什么是MongoDB的副本集(Replica Set)?
后端
Victor35618 小时前
MongoDB(8)什么是聚合(Aggregation)?
后端
yeyeye11119 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
Tony Bai19 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php
+VX:Fegn089520 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
程序猿阿伟20 小时前
《GraphQL批处理与全局缓存共享的底层逻辑》
后端·缓存·graphql
小小张说故事20 小时前
SQLAlchemy 技术入门指南
后端·python
识君啊20 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端
CaracalTiger21 小时前
如何解决Unexpected token ‘<’, “<!doctype “… is not valid JSON 报错问题
java·开发语言·jvm·spring boot·python·spring cloud·json