🚀Java 本地缓存实战:基于 Hutool TimedCache 的通用缓存工具类封装

1,968 阅读3分钟

在中小型系统中,某些数据我们希望缓存到本地内存中,提升访问效率,但又不想引入复杂的分布式缓存如 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 在轻量缓存场景下非常实用,适合本地缓存热点数据、接口返回、配置等内容。如果你想更灵活地封装,也可以按模块或业务类型区分不同缓存实例。


📌 建议:将该工具类纳入你团队的基础工具库中,统一使用、统一清理、统一封装,避免代码重复和缓存混乱。


👀 有什么关于本地缓存实战的问题,欢迎评论区交流!