四、常用工具之Guava Cache

网友投稿 1027 2022-11-25

四、常用工具之Guava Cache

四、常用工具之Guava Cache

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

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

com.google.guava guava 20.0

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 { 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 cache = null; /** * 用于初始化缓存(某些场景下使用,例如系统启动检测缓存加载是否正常) */ public abstract void loadValueWhenStarted(); /** * 定义缓存值过期时的计算方法:一般是缓存过期时,重新读取数据库,缓存数据库中的数值(具体视情况而变) * 新值计算失败时抛出异常,get操作将继续返回旧的缓存 * @param key 缓存的key * @return 缓存值 * @throws Exception 异常 */ protected abstract V getValueWhenExpired(K key) throws Exception; private LoadingCache getCache(){ //单例模式中的双重检查锁 if (cache == null){ synchronized (this){ if (cache == null){ CacheBuilder cacheBuilder = CacheBuilder.newBuilder().maximumSize(maxSize); //设置缓存刷新周期 if (refreshDuration > 0){ cacheBuilder.refreshAfterWrite(refreshDuration, refreshTimeUnit); } //设置缓存过期周期 if (expirationDuration > 0){ cacheBuilder.expireAfterWrite(expirationDuration, expirationTimeUnit); } cache = cacheBuilder.build(new CacheLoader() { @Override public V load(K k) throws Exception { //为null时,会抛出异常 return getValueWhenExpired(k); } @Override public ListenableFuture reload(final K key, V oldValue) throws Exception { return refreshThreadPool.submit(new Callable() { @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 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 { 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; } }

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:八、“where g.id=m.id”与“LEFT JOIN sys_menu m ON g.id=m.id”区别?
下一篇:二、删除ZTree中的所有父节点与用户组的关联关系(删除多重复杂关系)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~