前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >使用 Guava 实现简洁高效的本地缓存

使用 Guava 实现简洁高效的本地缓存

原创
作者头像
用户1142828
发布2024-11-30 22:55:44
发布2024-11-30 22:55:44
860
举报

使用 Guava 实现简洁高效的本地缓存

在现代应用中,缓存是提升性能和降低外部系统压力的关键组件。Google 提供的 Guava 是一款强大的 Java 工具库,其中的 Guava Cache 模块提供了灵活的本地缓存功能。

本文将通过封装 Guava Cache,实现一个通用的缓存模板,帮助开发者快速构建可复用的缓存逻辑。


为什么封装 Guava Cache?

Guava Cache 功能强大,但如果每次使用都需要手动配置,可能会显得繁琐。通过封装一个抽象类,我们可以将常用的配置和逻辑提取出来,让子类只需实现数据加载逻辑即可,简化代码,提高复用性。


封装 Guava Cache 的通用模板

以下是优化后的 GuavaAbstractLoadingCache 实现:

代码实现

代码语言:java
复制
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * 通用的 Guava 缓存模板。
 * 子类只需实现 fetchData(key) 方法,从外部数据源获取数据。
 * 调用 getValue(key) 即可从缓存中获取数据,缓存失效时会自动加载。
 *
 * @param <K> 缓存键类型(必须实现 Serializable)
 * @param <V> 缓存值类型
 */
public abstract class GuavaAbstractLoadingCache<K extends Serializable, V> {
    private static final Logger logger = Logger.getLogger(GuavaAbstractLoadingCache.class.getName());

    private final int maximumSize;
    private final int expireAfterWriteDuration;
    private final TimeUnit timeUnit;

    private volatile LoadingCache<K, V> cache;

    /**
     * 默认构造函数,使用默认配置。
     */
    protected GuavaAbstractLoadingCache() {
        this(1000, 60, TimeUnit.MINUTES);
    }

    /**
     * 自定义配置构造函数。
     *
     * @param maximumSize             最大缓存条数
     * @param expireAfterWriteDuration 缓存过期时长
     * @param timeUnit                时间单位
     */
    protected GuavaAbstractLoadingCache(int maximumSize, int expireAfterWriteDuration, TimeUnit timeUnit) {
        this.maximumSize = maximumSize;
        this.expireAfterWriteDuration = expireAfterWriteDuration;
        this.timeUnit = timeUnit;
    }

    /**
     * 获取缓存实例。如果缓存未初始化,则进行初始化。
     *
     * @return 缓存实例
     */
    private LoadingCache<K, V> getCache() {
        if (cache == null) {
            synchronized (this) {
                if (cache == null) {
                    cache = CacheBuilder.newBuilder()
                            .maximumSize(maximumSize)
                            .expireAfterWrite(expireAfterWriteDuration, timeUnit)
                            .build(new CacheLoader<K, V>() {
                                @Override
                                public V load(K key) throws Exception {
                                    return fetchData(key);
                                }
                            });
                    logger.info("缓存初始化成功:最大大小 = " + maximumSize + ", 过期时长 = " + expireAfterWriteDuration + " " + timeUnit);                }
            }
        }
        return cache;
    }

    /**
     * 子类实现:从外部数据源获取数据。
     *
     * @param key 缓存键
     * @return 数据值
     * @throws Exception 如果获取数据失败
     */
    protected abstract V fetchData(K key) throws Exception;

    /**
     * 从缓存中获取数据。若缓存失效或不存在,会调用 fetchData 自动加载。
     *
     * @param key 缓存键
     * @return 数据值
     */
    public V getValue(K key) {
        try {
            return getCache().get(key);
        } catch (ExecutionException e) {
            logger.log(Level.SEVERE, MessageFormat.format("缓存加载失败, key: {0}", key), e);
            throw new RuntimeException("缓存加载失败", e);
        }
    }

    /**
     * 清空缓存并重新初始化。
     */
    public synchronized void resetCache() {
        if (cache != null) {
            cache.invalidateAll();
            cache = null;
            logger.info("缓存已清空并重置");
        }
    }
}

优化亮点

  1. 简化逻辑
    • 删除了复杂的统计逻辑,如 highestSizehighestTime
    • 移除了冗余的 setter 和 getter 方法,参数通过构造函数一次性确定。
  2. 默认配置
    • 提供默认构造函数(1000 条、60 分钟过期),简化子类使用。
  3. 清晰的结构
    • 缓存初始化采用双重校验锁(DCL),线程安全且性能优越。
    • 错误处理集中在 getValue 方法中,避免调用方处理过多异常。

使用示例:用户信息缓存

通过继承 GuavaAbstractLoadingCache,我们可以快速实现具体的缓存逻辑。以下是一个简单的用户信息缓存示例。

示例子类

代码语言:java
复制
import java.util.HashMap;
import java.util.Map;

/**
 * 示例子类:实现一个用户信息缓存。
 */
public class UserCache extends GuavaAbstractLoadingCache<String, String> {
    private static final Map<String, String> DATABASE = new HashMap<>();

    static {
        DATABASE.put("1", "Alice");
        DATABASE.put("2", "Bob");
        DATABASE.put("3", "Charlie");
    }

    @Override
    protected String fetchData(String key) {
        System.out.println("从外部数据源加载数据,key: " + key);
        return DATABASE.getOrDefault(key, "Unknown User");
    }
}

测试代码

代码语言:java
复制
public class CacheExample {
    public static void main(String[] args) {
        // 创建缓存实例
        UserCache userCache = new UserCache();

        // 测试获取数据
        System.out.println(userCache.getValue("1")); // 输出:Alice
        System.out.println(userCache.getValue("2")); // 输出:Bob
        System.out.println(userCache.getValue("4")); // 输出:Unknown User

        // 测试缓存功能(第二次获取不需要从数据源加载)
        System.out.println(userCache.getValue("1")); // 输出:Alice (缓存命中)

        // 清空缓存并重新获取数据
        userCache.resetCache();
        System.out.println(userCache.getValue("1")); // 输出:Alice (重新加载)
    }
}

运行结果

代码语言:plaintext
复制
从外部数据源加载数据,key: 1
Alice
从外部数据源加载数据,key: 2
Bob
从外部数据源加载数据,key: 4
Unknown User
Alice
缓存已清空并重置
从外部数据源加载数据,key: 1
Alice

Maven 依赖

pom.xml 中添加以下依赖即可使用 Guava 和日志功能:

代码语言:xml
复制
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>

参考文献

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用 Guava 实现简洁高效的本地缓存
    • 为什么封装 Guava Cache?
    • 封装 Guava Cache 的通用模板
      • 代码实现
      • 优化亮点
    • 使用示例:用户信息缓存
      • 示例子类
      • 测试代码
      • 运行结果
    • Maven 依赖
    • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档