一、先引入依赖
<!--guava缓存-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
二、测试demo
public class GuavaCacheDemo {
//模拟Guava缓存
private static class DataSource {
// 模拟数据获取方法
public String getValue(String key) {
//模拟从数据库或其他数据源获取数据
System.out.println("[数据源]正在查询key: " + key);
try {
TimeUnit.MILLISECONDS.sleep(200); // 模拟延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回模拟数据
return "value_" + key;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
DataSource dataSource = new DataSource();
//1.构建缓存实例(核心配置)
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100) // 最大容量
.expireAfterWrite(5, TimeUnit.SECONDS) // 写入后5秒过期
.refreshAfterWrite(3, TimeUnit.SECONDS) // 写入后3秒刷新
.recordStats() // 开启统计
// 3.配置缓存移除监听器
.removalListener(notification -> {
System.out.println("[缓存移除] key: " + notification.getKey() + ", 原因 : " + notification.getCause());
//2.配置缓存加载逻辑
}).build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return dataSource.getValue(key);
}
});
//3.使用缓存
System.out.println("===============第一次查询(缓存未命中)===============");
System.out.println("结果:" + cache.get("key1"));
System.out.println("===============第二次查询(缓存命中)===============");
System.out.println("结果:" + cache.get("key1"));
//手动输入缓存
cache.put("key2", "shoudong key2");
System.out.println("===============查询手动放入的缓存===============");
System.out.println("结果:" + cache.get("key2"));
//等待6秒,超过过期时间
System.out.println("===============等待6秒,超过过期时间===============");
TimeUnit.SECONDS.sleep(6);
System.out.println("===============第三次查询(缓存过期,重新加载)===============");
System.out.println("结果:" + cache.get("key1"));
//打印缓存统计信息
System.out.println("===============缓存统计信息===============");
CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate());
System.out.println("命中次数: " + stats.hitCount());
System.out.println("未命中次数: " + stats.missCount());
System.out.println("淘汰次数:" + stats.evictionCount());
//手动清除某个缓存
cache.invalidate("key2");
System.out.println("===============手动清除key2缓存后再次查询===============");
System.out.println("结果:" + cache.get("key2"));
//关闭缓存(可选)
cache.cleanUp();
}
}
三、测试结果:
===============第一次查询(缓存未命中)===============
[数据源]正在查询key: key1
结果:value_key1
===============第二次查询(缓存命中)===============
结果:value_key1
===============查询手动放入的缓存===============
结果:shoudong key2
===============等待6秒,超过过期时间===============
===============第三次查询(缓存过期,重新加载)===============
[缓存移除] key: key1, 原因 : EXPIRED
[缓存移除] key: key2, 原因 : EXPIRED
[数据源]正在查询key: key1
结果:value_key1
===============缓存统计信息===============
命中率: 0.5
命中次数: 2
未命中次数: 2
淘汰次数:2
===============手动清除key2缓存后再次查询===============
[数据源]正在查询key: key2
结果:value_key2
四、适用场景
-
热点数据本地缓存:如电商商品详情、配置信息、用户基础信息(查询频繁、修改少);
-
减轻数据源压力:对数据库/RPC接口的查询结果缓存,减少下游服务调用量;
-
短期有效数据:如临时生成的令牌、会话信息(需设置过期时间);
-
单机场景:不依赖分布式缓存(如 Redis),追求低延迟(本地内存查询)。
五、关键注意点
-
不支持分布式:仅存于当前应用进程,多实例部署会有数据一致性问题;
-
内存限制:需配置 maximumSize 避免内存溢出,依赖 JVM 垃圾回收;
-
过期策略:expireAfterWrite(写入后过期)、expireAfterAccess(访问后过期)按需选择。
947

被折叠的 条评论
为什么被折叠?



