最近在工作中使用到了Google Guava缓存功能,感觉还是不错,这周末顺便把它深入一点的学习了一下。
我们为什么需缓存?主要原因是:当计算或获取一个值的时候代价很高,或者频繁获取一个值的时候,我们为了提高系统的性能,就得考虑使用缓存了。而Google Guava的缓存功能就是一种选择。
那Google Guava缓存适合什么场景用呢?Google Guava官方文档(翻译版)这么说的:
(1)你愿意消耗一些内存空间来提升速度。
(2)你预料到某些键会被查询一次以上。
(3)缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)
只要满足上面任何一点,我们就可以使用Google Guava的缓存功能了。
现在我们跟着一个demo来了解下它吧。
1、导入依赖
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>25.1-jre</version> </dependency>
2、编写缓存工具类(核心)
private static Logger logger = LoggerFactory.getLogger(GuavaUtil.class); private static RemovalListener<Object, Object> removalListener = new RemovalListener<Object, Object>() { @Override public void onRemoval(RemovalNotification<Object, Object> removal) { logger.info(" [{}]被移除原因是 [{}] ",removal.getKey(),removal.getCause()); } }; public static LoadingCache<Object,Object> localCache = CacheBuilder.newBuilder() .initialCapacity(2) .maximumSize(3) .expireAfterAccess(1, TimeUnit.MINUTES) .expireAfterWrite(1,TimeUnit.MINUTES) .removalListener(removalListener) .build(new CacheLoader<Object, Object>() { @Override public Object load(Object o) throws Exception { return null; } }); public static void setKey(Object key,Object value){ localCache.put(key,value); logger.info("[{}]缓存成功",key); } public static Object getKey(Object key,Object defultValue){ try { return localCache.get(key); } catch (ExecutionException e) { logger.info("获取缓存异常",e); }catch (CacheLoader.InvalidCacheLoadException e){ logger.info("没有从缓存中获取到数据,返回为null----"); } return defultValue; }
知识点:
(1)LoadingCache是附带CacheLoader构建而成的缓存实现。
(2)initialCapacity(long) 设置缓存的初始容量
(3) maximumSize(long) 设置缓存项目数的最大值,设置它 缓存将会尝试回收不常用的缓存,当然在达到这个限定值之前就可能进行回收操作了。
(4)expireAfterAccess(long, TimeUnit) 设置定时回收,在给定的时间内没有读/写的访问,则会回收
(5)expireAfterWrite(30,TimeUnit.MINUTES) 设置定时回收,缓存项在给定时间内没有被写访问(创建或覆盖),则回收。
(6)removalListener(RemovalListener) 移除监听事件,当缓存被移除时,会出发这个监听事件。
(7)localCache.put(key,value)向缓存中插入键和值,在demo中我自定义一个setKey()方法来进行插入缓存
(8)localCache.get(key) 从缓存中取值,在demo中自定义了getKey()方法,对取值进行了一些操作。
注意点:
guava不允许返回为null,否则会有如下异常
com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key name.
解决办法: (1) 在load()方法中设置 return "null"
(2) 在getKey()方法中捕捉该异常 并返回null或者传来的默认值
3、测试
@Controller public class GuavaController { private static org.slf4j.Logger logger = LoggerFactory.getLogger(GuavaUtil.class); Map<String, String> map = new HashMap<>(); { map.put("name", "xsh"); map.put("age", "23"); map.put("gender", "male"); } @RequestMapping("/guava/{key}") @ResponseBody public String guavaTest(@PathVariable("key") String key) { String result; if ((GuavaUtil.getKey(key, null)) == null) { GuavaUtil.setKey(key,map.get(key)); result= map.get(key); } else { result= (String) GuavaUtil.getKey(key, null); logger.info("从缓存中获取[{}]的值为:[{}]",key,result); } return result; } }
说明:为了测试方便使用了map集合代替了数据库。首先从缓存中查找是否存在key,如果不存在则加入到缓存中,如果存在则从缓存中取。
测试结果:
本次demo中只使用了缓存的基本功能。详细内容可以查看并发编程网翻译的Google Guava缓存的官网文档:http://ifeve.com/google-guava-cachesexplained/
本文demo的代码:从github上下载demo:https://github.com/520xsh/Guava.git