如何将基于Caffeine的Java Spring Cache设置为每日0点和12点定时过期刷新?
实现Caffeine Cache每日固定时间点(0点/12点)过期刷新
当然可以实现!你当前用的expireAfterWrite(12, TimeUnit.HOURS)是相对过期策略(写入后固定时长过期),而你需要的是绝对固定时间点过期——不管缓存什么时候写入,都只在每日的0点和12点触发过期刷新。Caffeine支持通过自定义Expiry接口来实现这种自定义过期逻辑,下面是具体的实现方案:
步骤1:自定义固定时间过期策略
我们需要实现Expiry接口,重写三个方法来统一返回缓存到下一个0点或12点的剩余时长。用Java 8的LocalDateTime处理时间计算会更清晰直观:
import com.github.benmanes.caffeine.cache.Expiry; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; public class DailyFixedTimeExpiry<K, V> implements Expiry<K, V> { @Override public long expireAfterCreate(K key, V value, long currentTime) { // 计算当前时间到下一个0点/12点的毫秒数 return calculateNextExpiryMillis(); } @Override public long expireAfterUpdate(K key, V value, long currentTime, long currentDuration) { // 更新缓存时,重新对齐到下一个固定过期点 return calculateNextExpiryMillis(); } @Override public long expireAfterRead(K key, V value, long currentTime, long currentDuration) { // 读取缓存不改变过期时间,保持原有的固定过期点 return currentDuration; } private long calculateNextExpiryMillis() { LocalDateTime now = LocalDateTime.now(); LocalDateTime nextExpiry; // 根据当前时间判断下一个过期点 if (now.getHour() < 12) { // 当前早于12点,下一个过期点是当天12点整 nextExpiry = now.withHour(12).withMinute(0).withSecond(0).withNano(0); } else { // 当前晚于/等于12点,下一个过期点是次日0点整 nextExpiry = now.plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0); } // 计算两个时间点的毫秒差 return ChronoUnit.MILLIS.between(now, nextExpiry); } }
步骤2:修改CacheConfig配置
把原来的expireAfterWrite替换为expireAfter,传入我们自定义的过期策略即可:
import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Component; @Component @EnableCaching public class CacheConfig { @Bean public Caffeine defaultCacheConfig() { return Caffeine.newBuilder() // 启用自定义的固定时间过期策略 .expireAfter(new DailyFixedTimeExpiry<>()); } }
关键逻辑说明
expireAfterCreate:缓存创建时直接计算到下一个固定时间点的剩余时长,确保缓存只会在0点/12点过期expireAfterUpdate:更新缓存时重新计算过期时间,避免更新后打乱固定过期节奏expireAfterRead:读取缓存不修改过期时间,防止频繁读取导致过期点被无端重置- 时间计算:通过判断当前小时数,自动匹配最近的0点或12点,确保缓存严格对齐你需要的刷新时间
额外注意事项
- 时区适配:上面的代码使用系统默认时区,如果需要指定特定时区(比如UTC),可以在
LocalDateTime.now()时传入ZoneId,例如LocalDateTime.now(ZoneId.of("UTC")) - 自动刷新:如果需要缓存过期后自动加载最新数据,需要结合
CacheLoader使用(比如Caffeine.newBuilder().build(cacheLoader)),这样缓存过期时会自动调用加载方法刷新数据 - 测试验证:可以通过模拟不同时间点创建缓存,查看过期时长是否符合预期(比如上午10点创建,过期时长应为3小时)
内容的提问来源于stack exchange,提问作者Brian Akumah
相关产品推荐
相关产品推荐

