当前位置: 首页 > 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());}}

相关文章:

  • R语言助力森林生态研究:从数据处理到群落稳定性分析的完整流程,结合机器学习与案例写作
  • Kali Linux 安装 Rust 环境简明教程
  • js获取uniapp获取webview内容高度
  • 从零实战:在Xilinx Zynq PS端移植VxWorks 6.9系统
  • uniapp 全局混入:监听路由变化,路由变化即执行
  • oceanbase不兼容SqlSugarCore的问题
  • 【25软考网工】第五章(7)路由协议、静态与默认路由、路由协议分类
  • 电动加长杆金属硬密封法兰式蝶阀泄漏等级解析:水、蒸汽、油品介质的可靠选择-耀圣
  • AI+浏览器自动化:Nanobrowser Chrome 扩展的使用「详细教程」
  • 如何建设网站?网站建设简单步骤有哪些?
  • Webpack 5 Module Federation 深度解析
  • k8s术语之service
  • 开源模型应用落地-qwen模型小试-Qwen3-8B-推理加速-vLLM-结构化输出(三)
  • 遥控器网络推拉流技术要点!
  • 荣耀A8互动娱乐组件部署实录(第4部分:房间服务端逻辑与玩家交互处理)
  • 纷析云开源财务软件:重新定义企业财务自主权
  • 数字传播生态中开源链动模式与智能技术协同驱动的品牌认知重构研究——基于“开源链动2+1模式+AI智能名片+S2B2C商城小程序”的场景化传播实践
  • 《面向对象程序设计-C++》实验五 虚函数的使用及抽象类
  • 深入理解 Linux 阻塞IO与Socket数据结构
  • 目标检测(Object Detection)研究方向常用数据集简单介绍
  • 马上评|不再提“智驾”,新能源车企回归理性
  • 哥伦比亚总统称将在访华期间签署“一带一路”倡议意向书,外交部回应
  • 外交部回应西班牙未来外交战略:愿与之一道继续深化开放合作
  • A股三大股指收涨:军工股掀涨停潮,两市成交近1.5万亿元
  • “80后”海南琼海市长傅晟,去向公布
  • 陕南多地供水形势严峻:有的已呼吁启用自备水井