在中小型系统中,某些数据我们希望缓存到本地内存中,提升访问效率,但又不想引入复杂的分布式缓存如 Redis。这时,Hutool 的 TimedCache 是个非常轻巧好用的方案。本文将基于实际项目封装一个通用的本地缓存工具类,并介绍其中的关键点和注意事项。
💡 为什么使用 TimedCache?
Hutool 是一套 Java 工具库,里面的 TimedCache 提供了带有效期的本地缓存机制:
- 支持设置超时时间;
- 支持按需刷新访问时间;
- 支持定时清理;
- 性能轻快,依赖少,适合单体服务或工具场景。
🛠️ 实战代码:封装一个通用缓存工具类
下面是我们封装的 HutoolCacheUtil,功能包括:
- 缓存写入、读取(支持泛型)、删除
- 支持是否刷新访问时间
- 支持自定义缓存项超时时间
- 支持定时清理过期数据(非常重要)
public class HutoolCacheUtil {
// 缓存 - 全局默认过期时间30分钟
private static final TimedCache<String, Object> timedCache = CacheUtil.newTimedCache(1000 * 60 * 30);
// 定时清理 - 每30分钟清理一次过期缓存(建议一定设置!)
static {
timedCache.schedulePrune(1000 * 60 * 30);
}
public static boolean containsKey(String key) {
return timedCache.containsKey(key);
}
// 每次调用会重置过期时间。适用于频繁访问的缓存,不关心缓存时效性
public static Object get(String key) {
return timedCache.get(key);
}
/**
* 获取缓存内容,可选择是否刷新过期时间
* @param key key
* @param isUpdateLastAccess 是否重置过期时间
*/
@SuppressWarnings("unchecked") // Java 泛型擦除,需手动转换
public static <T> T get(String key, boolean isUpdateLastAccess) {
return (T) timedCache.get(key, isUpdateLastAccess);
}
public static void put(String key, Object value) {
_globalTimedCache.put(key, value);
}
public static void put(String key, Object value, Integer timeout) {
_globalTimedCache.put(key, value, timeout);
}
public static void remove(String key) {
_globalTimedCache.remove(key);
}
public static void removeKeys(String keyword) {
Set<String> keySet = _globalTimedCache.keySet();
keySet.forEach(k -> {
if (k.contains(keyword)) {
_globalTimedCache.remove(k);
}
});
}
}
⚠️ 使用时你必须注意的坑
1️⃣ schedulePrune 是必须的
并且要注意get(key)方法是重置过期时间的,如果想避开这个机制,请调用get("key1", false)方法。
Hutool 的缓存虽然支持超时机制,但不会自动清理已过期的项,默认只在访问时做清理。
如果你忘记设置 schedulePrune(...),过期缓存会常驻内存,导致内存占用越来越高。
// 每30分钟清理一次过期条目(建议与默认过期时间一致)
_globalTimedCache.schedulePrune(1000 * 60 * 30);
2️⃣ 泛型类型擦除问题
由于 Java 泛型擦除机制,缓存返回值需要强制类型转换,可能会出现警告:
List<MyDTO> list = (List<MyDTO>) HutoolCacheUtil.get("myListKey");
我们在工具类中加了:
@SuppressWarnings("unchecked")
public static <T> T get(String key, boolean isUpdateLastAccess)
@SuppressWarnings("unchecked")这个注解告诉编译器“我知道你警告的是什么,这里我可以接受”,是安全的,只要你自己控制了 put 和 get 的类型一致。
3️⃣ 不能用于分布式缓存!
这是本地内存缓存,多个服务节点之间是互相不可见的。如果你有分布式部署场景,建议使用 Redis、Caffeine + Redis 等双缓存机制。
📌 使用示例
// 缓存一个列表对象30分钟
List<MyDTO> list = queryFromDB();
HutoolCacheUtil.put("myList", list);
// 获取时自动重置过期时间(续命)
List<MyDTO> cachedList = (List<MyDTO>) HutoolCacheUtil.get("myList");
// 获取但不续命(即不刷新 TTL)
List<MyDTO> cachedList2 = HutoolCacheUtil.get("myList", false);
✅ 总结
| 功能 | 是否支持 |
|---|---|
| 设置缓存超时 | ✅ |
| 自动清理 | ✅(需手动开启) |
| 是否线程安全 | ✅ |
| 泛型支持 | ✅(通过抑制警告) |
| 适合分布式使用 | ❌ |
| 适合轻量场景 | ✅ |
Hutool 的 TimedCache 在轻量缓存场景下非常实用,适合本地缓存热点数据、接口返回、配置等内容。如果你想更灵活地封装,也可以按模块或业务类型区分不同缓存实例。
📌 建议:将该工具类纳入你团队的基础工具库中,统一使用、统一清理、统一封装,避免代码重复和缓存混乱。
👀 有什么关于本地缓存实战的问题,欢迎评论区交流!