📦 1. ExpiringMap 是什么?

net.jodah:expiringmap 是一个 轻量级、高性能的 Java Map 实现,内置 key 过期机制。它非常适合作为 临时缓存、验证码存储、限流、token 存储 等用途。

特点:

  • 每个 key 可以设置独立过期时间

  • 支持两种过期策略:

    • CREATED:从创建开始计时
    • ACCESSED:每次访问都会重置 TTL
  • 自动清理过期 key

  • 支持过期回调(EntryExpiredListener)

  • 性能优于 ConcurrentHashMap + ScheduledExecutorService 的自制方案


📥 2. 引入依赖

Maven:

<dependency>
    <groupId>net.jodah</groupId>
    <artifactId>expiringmap</artifactId>
    <version>0.5.10</version>
</dependency>

Gradle:

implementation 'net.jodah:expiringmap:0.5.10'

🧰 3. 基本使用

⭐ 创建一个 ExpiringMap

ExpiringMap<String, String> map = ExpiringMap.builder()
        .expiration(10, TimeUnit.SECONDS) // 默认过期时间
        .variableExpiration()             // 允许每个 key 单独设置 TTL
        .build();

⭐ 放入带过期时间的 Key

map.put("token", "abc123", 5, TimeUnit.SECONDS);

⭐ 获取

String v = map.get("token");  // 5秒后会返回 null

⭐ 检查是否过期

过期 key 会被自动删除,但你也可以自己检查:

map.containsKey("token"); // false when expired

⏰ 4. 过期策略(ExpirationPolicy)

两种策略:

✔ CREATED(默认)

Key 写入后开始计时,每次访问不会延长 TTL

map.put("a", "1", ExpirationPolicy.CREATED, 5, TimeUnit.SECONDS);

✔ ACCESSED(滑动过期)

每次访问都会刷新 TTL → 类似会话(session)

map.put("user-session", "u1", ExpirationPolicy.ACCESSED, 30, TimeUnit.MINUTES);

🔔 5. 监听 Key 过期事件

监听器可以用于:

  • 清理资源
  • 打印日志
  • 执行回调

示例:

ExpiringMap<String, String> map = ExpiringMap.builder()
        .expiration(10, TimeUnit.SECONDS)
        .expirationListener((key, value) -> {
            System.out.println("Key expired: " + key + " => " + value);
        })
        .build();

🚀 6. 常见使用场景(真实“厂家”使用方法)

以下是企业工程中最常用的几种模式👇


🧩 6.1 验证码(SMS / 邮箱验证码)存储

map.put(phone, code, 2, TimeUnit.MINUTES);

使用方(厂家):
用于短信平台、Web 登录验证码校验等。


🧩 6.2 登录 Token / 临时 Token 存储

map.put("token-123", userId, ExpirationPolicy.CREATED, 30, TimeUnit.MINUTES);

厂家用途:

  • OAuth2 implicit token
  • 邮箱“临时访问链接”
  • 重置密码 token

🧩 6.3 防重处理(幂等去重)

map.put(requestId, true, 10, TimeUnit.SECONDS);

厂家用途:
支付回调、消息重复消费、HTTP 防重复提交。


🧩 6.4 限流(Rate Limiter)计数器

Integer count = map.getOrDefault(ip, 0);
map.put(ip, count + 1, ExpirationPolicy.CREATED, 1, TimeUnit.MINUTES);

厂家用途:
接口防刷、IP 限制、短信限流。


🧩 6.5 缓存热点数据(轻量缓存)

map.put("product:123", productJson, 30, TimeUnit.SECONDS);

厂家用途:
无需完整 Redis,可以作为服务内部本地缓存。


🧱 7. 和其他方案对比

方案 特点 性能 适用场景
ExpiringMap 内存缓存,精确 TTL 单机缓存、轻量级、临时数据
ConcurrentHashMap+ScheduledExecutor 手写实现 可定制但复杂
Caffeine 更强缓存(LRU+TTL) 最高 高并发、高性能缓存
Redis 分布式缓存 网络延迟 多节点共享缓存、跨服务

ExpiringMap 适合 轻量、单机、临时 的使用场景。


📚 8. 最佳实践

  • 不要放入大量数据(内存缓存)
  • 对高并发缓存建议用 Caffeine
  • 分布式系统不要把它当 Redis 用
  • 可配合 Spring 使用(作为服务内部缓存)

使用:

  • ⭐ 一个完整的使用案例(比如验证码服务)
  • ⭐ 封装一个 ExpiringMap 工具类
  • ⭐ 实现一个 TokenManager(登录 token)
  • ⭐ 教你用 ExpiringMap 做限流
  • ⭐ 教你用 ExpiringMap + Spring Boot 集成