本文详细介绍了 Google Guava 库中强大的本地缓存组件 ------ LoadingCache。从核心概念、创建方法到过期策略、缓存刷新等高级特性,文章通过清晰的代码示例进行了全面讲解。无论你是想解决高并发场景下的性能瓶颈,还是希望减少重复计算/IO 操作,这篇指南都能帮助你快速上手,为你的应用轻松集成一个高效、可靠的本地缓存方案。
目录
[基于Guava LoadingCache的缓存管理器](#基于Guava LoadingCache的缓存管理器)
[Guava LoadingCache 核心特性](#Guava LoadingCache 核心特性)
[1. 自动加载](#1. 自动加载)
[2. 缓存配置](#2. 缓存配置)
[3. 批量操作](#3. 批量操作)
[Guava LoadingCache 原理详解](#Guava LoadingCache 原理详解)
[1. 懒加载(Lazy Loading)](#1. 懒加载(Lazy Loading))
[2. 自动过期机制](#2. 自动过期机制)
[3. 缓存回收策略](#3. 缓存回收策略)
基于Guava LoadingCache的缓存管理器
完整实现 (GuavaCacheManager.java)
java
@Slf4j
@Component
public class CacheManager {
@Autowired
private TerminalInfoService terminalInfoService;
@Autowired
private ContainerInfoService containerInfoService;
// 使用LoadingCache实现自动加载
private LoadingCache<String, Object> loadingCache = CacheBuilder.newBuilder()
// 初始容量
.initialCapacity(100)
// 最大容量
.maximumSize(10000)
// 写入后10小时过期
.expireAfterWrite(10, TimeUnit.HOURS)
// 访问后10小时过期
.expireAfterAccess(10, TimeUnit.HOURS)
// 开启统计 记录缓存统计信息
.recordStats()
.build(new CacheLoader<String, Object>() {
@Override
@Nullable
public Object load(String key) throws Exception {
// 当缓存不存在时,自动调用此方法加载数据
log.info("自动加载缓存: {}", key);
return loadDataByKey(key);
}
});
/**
* 根据key获取缓存值(自动加载)
*/
public Object getDataByKey(String key) {
try {
return loadingCache.get(key);
} catch (Exception e) {
log.error("获取缓存失败: {}", key, e);
return null;
}
}
/**
* 根据key加载数据(CacheLoader使用)
*/
public Object loadDataByKey(String key) throws Exception {
if (key.startsWith(CacheConstant.TERMINAL_IMEI)) {
return terminalInfoService.getByImei(getRealKey(key));
} else if (key.startsWith(CacheConstant.CONTAINER_TERMINAL_ID)) {
return containerInfoService.getByTerminalId(Long.valueOf(getRealKey(key)));
}
log.info("未知的key格式: " + key);
return null;
}
/**
* 分隔key标识,获取真实key
*
* @return
*/
private String getRealKey(String key) {
String[] keys = key.split(":");
return keys[1];
}
}
Guava LoadingCache 核心特性
1. 自动加载
java
// 当缓存不存在时自动调用load方法
Object value = loadingCache.get("user:1");
2. 缓存配置
java
CacheBuilder.newBuilder()
.maximumSize(1000) // 最大容量
.expireAfterWrite(30, TimeUnit.MINUTES) // 写入后过期时间
.expireAfterAccess(10, TimeUnit.MINUTES) // 访问后过期时间
.recordStats() // 开启统计
.build(cacheLoader);
3. 批量操作
java
// 批量获取(如果不存在会自动加载)
Map<String, Object> values = loadingCache.getAll(keys);
// 批量失效
loadingCache.invalidateAll(keys);
- 缓存统计
java
CacheStats stats = loadingCache.stats();
System.out.println("命中率: " + stats.hitRate());
System.out.println("加载次数: " + stats.loadCount());
使用场景
-
高频访问数据:用户信息、配置信息等
-
计算成本高的数据:需要复杂计算或远程调用的数据
-
数据一致性要求不高:可以接受短暂的数据延迟
Guava LoadingCache 原理详解
核心原理
Guava LoadingCache 采用的是 "按需加载" 机制,也就是 项目启动时不自动加载全部数据,当第一次查询时才去数据库获取并缓存,过期后自动移除。
工作原理示意图
XML
+----------------+ 1. get(key) +-----------------------+
| 客户端调用 | -------------------> | LoadingCache |
+----------------+ +-----------------------+
^ |
| | 2. 检查缓存是否存在
| |
| ↓
| +-----------------------+
| | 缓存命中? |
| +-----------------------+
| |
|是 |否
| |
| ↓
| +-----------------------+
| | 调用CacheLoader.load()|
| | 从数据源加载数据 |
| +-----------------------+
| |
| | 3. 加载数据
| ↓
| +-----------------------+
| | 存入缓存并返回结果 |
| +-----------------------+
| |
| |
+--------------------------------------+
核心特性解析
1. 懒加载(Lazy Loading)
java
LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
// 只有当缓存不存在时才会调用这个方法
System.out.println("正在加载数据: " + key);
return database.get(key);
}
});
// 第一次调用 - 触发加载
Object value1 = cache.get("key1"); // 输出: "正在加载数据: key1"
// 第二次调用 - 直接从缓存获取
Object value2 = cache.get("key1"); // 无输出,直接从缓存返回
2. 自动过期机制
java
LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期
.expireAfterAccess(5, TimeUnit.MINUTES) // 最后访问后5分钟过期
.build(cacheLoader);
// 设置后,缓存会自动管理过期和清理
3. 缓存回收策略
java
LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
.maximumSize(1000) // 基于容量
.weakKeys() // 使用弱引用键
.weakValues() // 使用弱引用值
.build(cacheLoader);
关键特点总结
-
懒加载:只有在第一次访问时才会加载数据
-
自动缓存:加载后的数据会自动缓存
-
过期自动移除:配置的过期时间到达后自动清理
-
线程安全:内置的线程安全机制
-
统计功能:提供详细的缓存命中率等统计信息
-
异常处理:加载失败时有相应的异常处理机制
这种设计非常适合以下场景:
-
数据量很大,无法全部加载到内存
-
数据访问有热点特征(部分数据被频繁访问)
-
可以接受第一次访问的延迟
-
数据变化不特别频繁