当前位置: 首页 > news >正文

Caffeine快速入门

依赖

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.2.0</version>
</dependency>

Cache的基本api操作

Caffeine.newBuilder.build来构建Caffeine

.maximumSize()设置最大缓存数量

expireAfterWrite(10, TimeUnit.SECONDS) 设置在缓存写后的10分钟过期

expireAfterAccess(10, TimeUnit.SECONDS)设置在缓存被访问后的10分钟过期

put("key", "value");//放入数据

getIfPresent("key-key");//获取数据,存在则返回值,不存在则返回Null

get("key2", k -> "into");//获取缓存,如果不存在则放入数据

invalidate("delete");//删除指定的缓存数据

invalidateAll();//删除所有缓存数据

cleanUp();//清理已经过期或者被标记为无效的缓存项

recordStats()//开启统计

  1. CacheStats stats = cache.stats();
  2. log.info("命中次数 / 总请求次数:{}",stats.hitRate());
  3. log.info("缓存驱逐的次数:{}",stats.evictionCount());
  4. log.info("加载均值所花费的平均时间:{}",stats.averageLoadPenalty());
@Test
void test1() {Cache<String, String> cacheWrite = Caffeine.newBuilder().maximumSize(100)//设置缓存的最大条目数.expireAfterWrite(10, TimeUnit.SECONDS)//设置在缓存写后的10分钟过期.build();Cache<String, String> cacheAccess = Caffeine.newBuilder().maximumSize(100)//设置缓存的最大条目数.expireAfterAccess(10, TimeUnit.SECONDS)//设置在缓存被访问后的10分钟过期.build();cacheWrite.put("key", "value");//放入数据cacheWrite.getIfPresent("key-key");//获取数据,存在则返回值,不存在则返回NullcacheWrite.get("key2", k -> "into");//获取缓存,如果不存在则放入数据cacheWr.aite.invalidate("delete");//删除指定的缓存数据cacheWrite.invalidateAll();//删除所有缓存数据cacheWrite.cleanUp();//清理已经过期或者被标记为无效的缓存项//拿出统计信息Cache<String,String> cache = Caffeine.newBuilder().maximumSize(10_000).recordStats()//开启统计.build();CacheStats stats = cache.stats();log.info("命中次数 / 总请求次数:{}",stats.hitRate());log.info("缓存驱逐的次数:{}",stats.evictionCount());log.info("加载均值所花费的平均时间:{}",stats.averageLoadPenalty());}

LoadingCache的自动刷新

refreshAfterWrite(Duration.ofMinutes(1)) 写入一分钟后触发自动刷新

build(刷新方法)

//LoadingCache支持自动加载数据
//可以在构建 LoadingCache 时指定一个 CacheLoader
//当尝试获取一个不存在的缓存项时,LoadingCache 会自动调用 CacheLoader 来加载数据,并将加载的数据存入缓存,然后返回该数据
@Test
void test2() {// 创建自动加载缓存LoadingCache<String, String> cache = Caffeine.newBuilder().maximumSize(100).refreshAfterWrite(Duration.ofMinutes(1))//写入一分钟后触发自动刷新.build(key -> loadFromDatabase(key)); // 缓存未命中时自动调用此方法// 使用缓存String value = cache.get("user101"); // 自动加载System.out.println(value);
}private static String loadFromDatabase(String key) {// 模拟数据库查询System.out.println("Loading from DB: " + key);return "data_for_" + key;
}

移除监听器

removalListener()

//移除监听器
@Test
void test3() {// 创建移除监听器RemovalListener<String, String> removalListener = (key, value, cause) -> {System.out.printf("Key %s was removed (%s), value: %s%n", key, cause, value);};// 创建 Caffeine 缓存并设置移除监听器Cache<String, String> cache = Caffeine.newBuilder().maximumSize(2).removalListener(removalListener).build();// 向缓存中添加元素cache.put("key1", "value1");cache.put("key2", "value2");cache.put("key3", "value3");
}

写入监听器

//写入监听器
@Test
void test4() {Cache<String, String> cache = Caffeine.newBuilder().maximumSize(2).removalListener((String key, String value, RemovalCause cause) -> {// 根据 cause 执行不同逻辑switch (cause) {case EXPLICIT:System.out.printf("[手动删除] Key=%s, Value=%s%n", key, value);break;case SIZE:System.out.printf("[容量驱逐] Key=%s (当前值可能已过时)%n", key);break;case EXPIRED:System.out.printf("[过期失效] Key=%s%n", key);break;case REPLACED:System.out.printf("[值被覆盖(写入)] Key=%s (旧值: %s)%n", key, value);break;default:System.out.printf("[其他原因] Key=%s (原因: %s)%n", key, cause);}}).build();// 测试不同场景cache.put("k1", "v1");cache.put("k2", "v2");cache.put("k3", "v3");  // 触发 SIZE 驱逐 k1cache.invalidate("k2"); // 触发 EXPLICIT 删除cache.put("k3", "v3_new"); // 触发 REPLACED
}

定制化缓存清除策略

expireAfter()

//定制化缓存清除策略
void test5() {Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).expireAfter(new Expiry<String, String>() {@Overridepublic long expireAfterCreate(@NonNull String s, @NonNull String s2, long l) {log.info("[创建后失效计算 key = {}, value = {}]", s, s2);// 相当于创建后多少秒就失效了return TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);}@Overridepublic long expireAfterUpdate(@NonNull String s, @NonNull String s2, long l, long l1) {log.info("[更新后失效计算 key = {}, value = {}]", s, s2);// 更新完多少秒后就失效了return TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS);}@Overridepublic long expireAfterRead(@NonNull String s, @NonNull String s2, long l, long l1) {log.info("[读取后失效计算 key = {}, value = {}]", s, s2);// 读取完多少秒后就失效了return TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);  // 将2秒转成纳秒}})   // 第一步.build();cache.put("Bob", "已登录");cache.put("Lily", "未登录");cache.put("Wang", "未登录");cache.put("Lee", "已登录");log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily"));log.info("获取缓存数据,Wang= {}", cache.getIfPresent("Wang"));log.info("获取缓存数据,Lee= {}", cache.getIfPresent("Lee"));}

基于权重的缓存清除策略

weigher()

//基于权重的缓存清除策略,总权重大于配置数量时,Caffeine 会优先淘汰那些最近最少被访问的缓存项
@Test
void test6() throws InterruptedException {Cache<String, String> cache = Caffeine.newBuilder().maximumWeight(100).weigher((key, value) -> {log.info("[weigher权重计算器] key = {}, val = {}", key, value);return 50;}).expireAfterAccess(3L, TimeUnit.SECONDS).build();// 放入第一个缓存项,总权重为 50cache.put("key1", "value1");System.out.println("放入 key1 后,缓存大小: " + cache.estimatedSize());// 放入第二个缓存项,总权重为 100cache.put("key2", "value2");System.out.println("放入 key2 后,缓存大小: " + cache.estimatedSize());// 放入第三个缓存项,总权重大于 100,会触发淘汰cache.put("key3", "value3");System.out.println("放入 key3 后,缓存大小: " + cache.estimatedSize());// 等待 3 秒,让缓存项过期Thread.sleep(3000);cache.cleanUp();System.out.println("等待 3 秒后,缓存大小: " + cache.estimatedSize());
}

测试类源码

package com.example.kiratest.test;import com.github.benmanes.caffeine.cache.*;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import io.micrometer.common.lang.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.time.Duration;
import java.util.concurrent.TimeUnit;@SpringBootTest
@Slf4j
public class CaffeineTest {@Testvoid test1() {Cache<String, String> cacheWrite = Caffeine.newBuilder().maximumSize(100)//设置缓存的最大条目数.expireAfterWrite(10, TimeUnit.SECONDS)//设置在缓存写后的10分钟过期.build();Cache<String, String> cacheAccess = Caffeine.newBuilder().maximumSize(100)//设置缓存的最大条目数.expireAfterAccess(10, TimeUnit.SECONDS)//设置在缓存被访问后的10分钟过期.build();cacheWrite.put("key", "value");//放入数据cacheWrite.getIfPresent("key-key");//获取数据,存在则返回值,不存在则返回NullcacheWrite.get("key2", k -> "into");//获取缓存,如果不存在则放入数据cacheWrite.invalidate("delete");//删除指定的缓存数据cacheWrite.invalidateAll();//删除所有缓存数据cacheWrite.cleanUp();//清理已经过期或者被标记为无效的缓存项//拿出统计信息Cache<String,String> cache = Caffeine.newBuilder().maximumSize(10_000).recordStats()//开启统计.build();CacheStats stats = cache.stats();log.info("命中次数 / 总请求次数:{}",stats.hitRate());log.info("缓存驱逐的次数:{}",stats.evictionCount());log.info("加载均值所花费的平均时间:{}",stats.averageLoadPenalty());}//LoadingCache支持自动加载数据//可以在构建 LoadingCache 时指定一个 CacheLoader//当尝试获取一个不存在的缓存项时,LoadingCache 会自动调用 CacheLoader 来加载数据,并将加载的数据存入缓存,然后返回该数据@Testvoid test2() {// 创建自动加载缓存LoadingCache<String, String> cache = Caffeine.newBuilder().maximumSize(100).refreshAfterWrite(Duration.ofMinutes(1))//写入一分钟后触发自动刷新.build(key -> loadFromDatabase(key)); // 缓存未命中时自动调用此方法// 使用缓存String value = cache.get("user101"); // 自动加载System.out.println(value);}private static String loadFromDatabase(String key) {// 模拟数据库查询System.out.println("Loading from DB: " + key);return "data_for_" + key;}//移除监听器@Testvoid test3() {// 创建移除监听器RemovalListener<String, String> removalListener = (key, value, cause) -> {System.out.printf("Key %s was removed (%s), value: %s%n", key, cause, value);};// 创建 Caffeine 缓存并设置移除监听器Cache<String, String> cache = Caffeine.newBuilder().maximumSize(2).removalListener(removalListener).build();// 向缓存中添加元素cache.put("key1", "value1");cache.put("key2", "value2");cache.put("key3", "value3");}//写入监听器@Testvoid test4() {Cache<String, String> cache = Caffeine.newBuilder().maximumSize(2).removalListener((String key, String value, RemovalCause cause) -> {// 根据 cause 执行不同逻辑switch (cause) {case EXPLICIT:System.out.printf("[手动删除] Key=%s, Value=%s%n", key, value);break;case SIZE:System.out.printf("[容量驱逐] Key=%s (当前值可能已过时)%n", key);break;case EXPIRED:System.out.printf("[过期失效] Key=%s%n", key);break;case REPLACED:System.out.printf("[值被覆盖(写入)] Key=%s (旧值: %s)%n", key, value);break;default:System.out.printf("[其他原因] Key=%s (原因: %s)%n", key, cause);}}).build();// 测试不同场景cache.put("k1", "v1");cache.put("k2", "v2");cache.put("k3", "v3");  // 触发 SIZE 驱逐 k1cache.invalidate("k2"); // 触发 EXPLICIT 删除cache.put("k3", "v3_new"); // 触发 REPLACED}//定制化缓存清除策略void test5() {Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).expireAfter(new Expiry<String, String>() {@Overridepublic long expireAfterCreate(@NonNull String s, @NonNull String s2, long l) {log.info("[创建后失效计算 key = {}, value = {}]", s, s2);// 相当于创建后多少秒就失效了return TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);}@Overridepublic long expireAfterUpdate(@NonNull String s, @NonNull String s2, long l, long l1) {log.info("[更新后失效计算 key = {}, value = {}]", s, s2);// 更新完多少秒后就失效了return TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS);}@Overridepublic long expireAfterRead(@NonNull String s, @NonNull String s2, long l, long l1) {log.info("[读取后失效计算 key = {}, value = {}]", s, s2);// 读取完多少秒后就失效了return TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);  // 将2秒转成纳秒}})   // 第一步.build();cache.put("Bob", "已登录");cache.put("Lily", "未登录");cache.put("Wang", "未登录");cache.put("Lee", "已登录");log.info("获取缓存数据,Bob= {}", cache.getIfPresent("Bob"));log.info("获取缓存数据,Lily= {}", cache.getIfPresent("Lily"));log.info("获取缓存数据,Wang= {}", cache.getIfPresent("Wang"));log.info("获取缓存数据,Lee= {}", cache.getIfPresent("Lee"));}//基于权重的缓存清除策略,总权重大于配置数量时,Caffeine 会优先淘汰那些最近最少被访问的缓存项@Testvoid test6() throws InterruptedException {Cache<String, String> cache = Caffeine.newBuilder().maximumWeight(100).weigher((key, value) -> {log.info("[weigher权重计算器] key = {}, val = {}", key, value);return 50;}).expireAfterAccess(3L, TimeUnit.SECONDS).build();// 放入第一个缓存项,总权重为 50cache.put("key1", "value1");System.out.println("放入 key1 后,缓存大小: " + cache.estimatedSize());// 放入第二个缓存项,总权重为 100cache.put("key2", "value2");System.out.println("放入 key2 后,缓存大小: " + cache.estimatedSize());// 放入第三个缓存项,总权重大于 100,会触发淘汰cache.put("key3", "value3");System.out.println("放入 key3 后,缓存大小: " + cache.estimatedSize());// 等待 3 秒,让缓存项过期Thread.sleep(3000);cache.cleanUp();System.out.println("等待 3 秒后,缓存大小: " + cache.estimatedSize());}}
http://www.xdnf.cn/news/298387.html

相关文章:

  • Oracle02-安装
  • JavaScript 对象引用与值传递的奥秘
  • Acrel-EIoT 能源物联网云平台在能耗监测系统中的创新设计
  • 启发式算法-模拟退火算法
  • STM32的智慧农业系统开发(uC/OS-II)
  • 如何设计Kafka的高可用跨机房容灾方案?(需要实战,未实战,纯理论)
  • 破局者手册 Ⅱ:测试开发深度攻坚,引爆质量优化新动能!
  • ES6/ES11知识点 续四
  • 【自然语言处理与大模型】LlamaIndex的词嵌入模型和向量数据库
  • 奇瑞依托汽车产业链,实现服务机器人万台下线
  • 【计算机网络 第8版】谢希仁编著 第四章网络层 地址类题型总结
  • 前端-HTML+CSS+JavaScript+Vue+Ajax概述
  • UE5 诺伊腾动捕使用笔记
  • Vue + Element UI 表单弹窗输入法卡顿问题解决方案
  • 第二章:langchain文本向量化(embed)搭建与详细教程-本地服务方式(下)
  • 2025 后端自学UNIAPP【项目实战:旅游项目】2、安装下载引用前端UI框架:uview-plus
  • OSCP - Proving Grounds - NoName
  • window 显示驱动开发-线程和同步级别一级(二)
  • 基于深度学习的图像识别技术:从原理到应用
  • 【数据挖掘】Apriori算法
  • 【愚公系列】《Manus极简入门》021-音乐创作助手:“音符魔术师”
  • 数学复习笔记 3
  • 【Part 2安卓原生360°VR播放器开发实战】第三节|实现VR视频播放与时间轴同步控制
  • iOS开发架构——MVC、MVP和MVVM对比
  • 如何开始使用 Blender:Blender 3D 初学者指南和简介 怎么下载格式模型
  • java springboot deepseek流式对话集成示例
  • UE5 材质淡入淡出
  • 【数据结构】求有向图强连通分量的方法
  • 【开发工具】Window安装WSL及配置Vscode获得Linux开发环境
  • 虚拟现实视频播放器 2.6.1 | 支持多种VR格式,提供沉浸式观看体验的媒体播放器