四、常用工具之Guava Cache

本文介绍GuavaCache的使用方法,包括内存缓存的实现、定时异步刷新机制及简单抽象封装。通过创建缓存模板工具类,实现了缓存的统一管理和高效使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)介绍参见Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装

(二)使用样例
1.引入Jar包

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>

2.抽象一个“cache”模板工具类

package com.imooc.util.cache;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 *     利用guava实现的内存缓存。缓存加载之后永不过期,后台线程定时刷新缓存值。刷新失败时将继续返回旧缓存。
 * 在调用getValue之前,需要设置 refreshDuration, refreshTimeunit, maxSize 三个参数后台刷新线程池为该系
 * 统中所有子类共享,大小为20.
 * @author **
 * @date 2018/6/8 15:07
 */
public abstract class BaseGuavaCache<K, V> {
    private static final Logger LOGGER = LoggerFactory.getLogger(BaseGuavaCache.class);

    /**
     * 缓存自动刷新周期
     */
    protected int refreshDuration = 10;
    /**
     * 缓存自动刷新周期时间格式
     */
    protected TimeUnit refreshTimeUnit = TimeUnit.MINUTES;

    /**
     * 缓存过期周期(负数代表永不过期)
     */
    protected int expirationDuration = -1;
    /**
     * 缓存过期周期时间格式
     */
    protected TimeUnit expirationTimeUnit = TimeUnit.HOURS;
    /**
     * 缓存最大容量
     * 备注:maxiSize定义了缓存的容量大小,当缓存数量即将到达容量上线时,则会进行缓存回收,
     *       回收最近没有使用或总体上很少使用的缓存项。需要注意的是在接近这个容量上限时就
     *       会发生,所以在定义这个值的时候需要视情况适量地增大一点。
     */
    protected int maxSize = 4;
    /**
     * 缓存刷新线程池
     */
    protected static ListeningExecutorService  refreshThreadPool =
            MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(20));
    /**
     * 使用双重检查锁,设置为单例
     */
    private LoadingCache<K, V> cache = null;

    /**
     * 用于初始化缓存(某些场景下使用,例如系统启动检测缓存加载是否正常)
     */
    public abstract void loadValueWhenStarted();

    /**
     * 定义缓存值过期时的计算方法:一般是缓存过期时,重新读取数据库,缓存数据库中的数值(具体视情况而变)
     *     新值计算失败时抛出异常,get操作将继续返回旧的缓存
     * @param key 缓存的key
     * @return 缓存值
     * @throws Exception 异常
     */
    protected abstract V getValueWhenExpired(K key) throws Exception;

    private LoadingCache<K, V> getCache(){
        //单例模式中的双重检查锁
        if (cache == null){
            synchronized (this){
                if (cache == null){
                    CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder().maximumSize(maxSize);
                    //设置缓存刷新周期
                    if (refreshDuration > 0){
                        cacheBuilder.refreshAfterWrite(refreshDuration, refreshTimeUnit);
                    }
                    //设置缓存过期周期
                    if (expirationDuration > 0){
                        cacheBuilder.expireAfterWrite(expirationDuration, expirationTimeUnit);
                    }
                    cache = cacheBuilder.build(new CacheLoader<K, V>() {
                        @Override
                        public V load(K k) throws Exception {
                            //为null时,会抛出异常
                            return getValueWhenExpired(k);
                        }

                        @Override
                        public ListenableFuture<V> reload(final K key, V oldValue) throws Exception {
                            return refreshThreadPool.submit(new Callable<V>() {
                                @Override
                                public V call() throws Exception {
                                    return getValueWhenExpired(key);
                                }
                            });
                        }
                    });
                }
            }
        }
        return cache;
    }

    /**
     * 从cache中取数据
     * @param key 键
     * @return 值
     * @throws Exception 异常
     * 备注:该key对应的值在缓存中不存在或者过期,调用该方法就会抛出异常。这个方法必须显式抛出异常,
     *       以供业务层判断缓存是否存在以及是否过期
     */
    public V getValue(K key) throws Exception {
        try {
            return getCache().get(key);
        } catch (ExecutionException e){
            LOGGER.error("从内存缓存中获取内容时发生异常,key:" + key, e);
            throw e;
        }
    }

    /**
     * 从cache中取数据,若发生异常,则返回默认值
     * @param key 键
     * @return 值
     * @throws ExecutionException 异常
     */
    public V getValueOfDefault(K key, V defaultValue){
        try {
            return getCache().get(key);
        } catch (ExecutionException e) {
            LOGGER.error("从内存缓存中获取内容时发生异常,key:" + key, e);
            return defaultValue;
        }
    }
    public void put(K key, V value){
        cache.put(key, value);
    }

    /**
     * 设置缓存刷新周期(链式编程)
     */
    public BaseGuavaCache<K, V> setRefreshDuration(int refreshDuration) {
        this.refreshDuration = refreshDuration;
        return this;
    }
    /**
     * 设置缓存刷新周期时间单元(链式编程)
     */
    public void setRefreshTimeUnit(TimeUnit refreshTimeUnit) {
        this.refreshTimeUnit = refreshTimeUnit;
    }
    /**
     * 设置缓存过期周期(链式编程)
     */
    public void setExpirationDuration(int expirationDuration) {
        this.expirationDuration = expirationDuration;
    }
    /**
     * 设置缓存过期周期时间单元(链式编程)
     */
    public void setExpirationTimeUnit(TimeUnit expirationTimeUnit) {
        this.expirationTimeUnit = expirationTimeUnit;
    }
    /**
     * 设置缓存最大数量(链式编程)
     */
    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    /**
     * 清除所有缓存
     */
    public void clearAllCache(){
        this.getCache().invalidateAll();
    }
    /**
     * 清除指定缓存
     */
    public void clearCacheByKey(K key){
        this.getCache().invalidate(key);
    }

}

3.发送验证码缓存工具类

package com.imooc.util.cache;


import java.util.concurrent.TimeUnit;

/**
 * @author 潘畅
 * @date 2018/6/8 17:17
 */
public class CodeCache extends BaseGuavaCache<String, Object> {

    private static CodeCache codeCache;

    /**
     * 单例模式
     */
    public static CodeCache getInstance() {
        if (codeCache == null){
            synchronized (CodeCache.class){
                if (codeCache == null){
                    codeCache = new CodeCache();
                }
            }
        }
        return codeCache;
    }

    /**
     * 在这里初始化必要参数(比如过期时间,定期刷新时间,缓存最大条数等)
     */
    private CodeCache() {
        //初始化过期时间
        this.setExpirationDuration(1);
        this.setExpirationTimeUnit(TimeUnit.MINUTES);
        //不刷新缓存
        this.setRefreshDuration(-1);
        this.setMaxSize(1000);
    }

    @Override
    public void loadValueWhenStarted() {

    }

    /**
     * 关于这个方法,暂时我也不太清楚,只能暂时先调用“getValue(key)”方法
     */
    @Override
    protected Object getValueWhenExpired(String key) throws Exception {
        return getValue(key);
    }

}

4.业务使用

/**
     * 保存验证码进缓存
     * 备注:验证码的缓存是设置了过期时间的(1min),
     *           若“该手机号首次进入”或“该手机号对应的缓存已经过期”,此时调用“cache.get(key)”方法会抛出异常,
     *       此时需要保存手机号,验证码进缓存,返回true;
     *           若不抛出异常,则说明该缓存正处于有效期,用户在1min内重复请求发送验证码,无需保存进缓存,返回false
     * @param phone 手机号
     * @param verificationCode 验证码
     * @return 是否成功保存手机号、验证码进缓存
     */
    private boolean saveVerificationCode(String phone, String verificationCode) {
        CodeCache codeCache = CodeCache.getInstance();
        try {
            codeCache.getValue(phone);
            //若不报异常,说明已存在该缓存,且未过期,说明在1min内重复请求了
            return false;
        } catch (Exception e) {
            //当缓存过期或没有时,取值会报异常,说明此时可将验证码存入缓存
            codeCache.put(phone, verificationCode);
            return true;
        }
    }
一、Windows 7和Vista系统下使用Java 开发资源库报错或无法运行 在Windows 7和Vista系统下使用Java 开发资源库要设置Java 开发资源库.exe的兼容性。设置方法如下: 1、打开“Java 开发资源库”文件夹,右键单击“Java 开发资源库.exe”,在弹出的菜单中选择“属性”菜单。 2、在打开的属性窗口中选择“兼容性”选项卡,在兼容模式区域将“以兼容模式运行这个程序”复选框和“以管理员身份运行此程序”复选框选中。 3、单击“应用” 按钮,Java 开发资源库.exe兼容性设置完成。 二、运行Java 开发资源库出现“MSDATLST.OCX”控件注册的错误。 因为本体验版为免安装程序,在某些系统中可能出现控件注册的错误,解决办法如下: 1、在Java 开发资源库文件夹中打开“setup”文件夹。 2、运行“setup”文件夹下的setup.exe文件,安装控件。 3、安装完成后,运行开发资源库程序,控件注册错误问题解决。 三、以非管理员身份登录系统,运行Java 开发资源库报错或无法运行。 因为本体验版为免安装程序,如果程序中某些控件没有注册,以非管理员身份登录时又没有权限注册,则出现报错或无法运行的情况,解决办法如下: 1、以系统管理员身份登录,运行Java 开发资源库。 2、如果以系统管理员登录能正确运行,则之后以非管理员身份登录,也可以正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值