我们在使用JDK的本地缓存时,通常使用HashMap或者ConcurrentHashMap来做本地缓存。比如在只读数据(加载字典表数据),或者每个部署节点独立的数据时(长连接服务中,每个部署节点由于都是维护了不同的连接,每个连接的数据都是独立的)。
在以前加载字典表的时候,处理方式可能就是在系统启动的时候,把字典表或者配置表的信息一次全部加载到 HashMap,需要自己处理数据过期或者有效性问题。如果在 HashMap中没有,从数据库查出数据,数据库有数据在手动把数据放到HashMap中的过程都需要程序员自己处理。
使用Guava Cache,上述的从数据库查出数据,数据库有数据在手动把数据放到HashMap中的过程就不需要我们手动处理,我们只需要定义好从数据库取数据的逻辑即可。
下面是一个简单的使用示例,用properties代替数据库做数据源:
public class GuavaCache {
private PropertiesUtil putil = new PropertiesUtil();
LoadingCache<String, String> cache = null;
private void init() {
cache = CacheBuilder.newBuilder()
//cache最大容量
.maximumSize(2)
//缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
.expireAfterWrite(1, TimeUnit.MINUTES)
//声明一个监听器,以便缓存项被移除时做一些额外操作
.removalListener(new MyListener())
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return putil.get(key);
}
});
}
public static void main(String[] args) throws Exception {
GuavaCache cache = new GuavaCache();
cache.init();
cache.testCache();
}
private void testCache() {
try {
System.out.println("before:" + cache.size());
String name = cache.get("app.name");
cache.get("app.version");
cache.get("app.port");
System.out.println("after:" + cache.size());
System.out.println("name :" + name);
} catch (Exception e) {
e.printStackTrace();
}
}
private class MyListener implements RemovalListener<String, String> {
@Override
public void onRemoval(RemovalNotification<String, String> removalNotification) {
String key = removalNotification.getKey();
String value = removalNotification.getValue();
RemovalCause cause = removalNotification.getCause();
System.out.println("key:"+ key + ",value:" + value + " removed, cause:" + cause.toString());
}
}
}
执行结果如下:
before:0
key:app.name,value:MyApp removed, cause:SIZE
after:2
name :MyApp
在guava cache中,每次get的时候,缓存中如果没有,就通过我们实现的CacheLoader类中的load方法来加载数据到缓存中。
因为我们定义的cache最大容量为2(maximumSize),第三次get的时候,超过了最大的数据容量,移除数据时会回调 RemovalListener。