最近用到了Guava的 LoadingCache 作为本地缓存,发现这个类的两个方法 get 和 getIfPresent 于是写一篇博客记录下两者的不同。
先说结论:我们在使用LoadingCache类的适合,builder中会传入一个CacheLoader,这个load方法是用来从别的地方取值保存在内存中的。
使用get时,如果内存中没有值,会自动调用load方法,如果load方法返回的是null,那么get会抛出异常。
使用getIfPresent时,如果内存中没有值,不会调用load方法,而是直接返回null。
以下是用到的测试代码:
首先,定义了一个Repository类,这个类有个成员变量,就是我们的主角LoadingCache。这是一个很常见的LocalCache用法。主要讲下build中传入的CacheLoader。这边模拟了下从Redis中读取数据的过程。当这个load方法被调用时,去Redis中获取key对应的value。
Redis中获取的时候,如果userId大于0,则返回用户+id,否则返回null。
public class LocalCacheTest {
private Repository repository = new Repository();
@Test
public void test() {
Long id = 0L;
try {
get(id);
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
}
public void get(Long id) throws ExecutionException {
String cacheValue = repository.getFromCache(id);
if (StringUtils.isEmpty(cacheValue)) {
System.out.println("value get failed");
} else {
System.out.println(
String.format("cache value: {%d} - {%s}",id, cacheValue));
}
}
}
public class Repository {
private LoadingCache<Long, String> localCache = CacheBuilder.newBuilder()
.maximumSize(CacheConstants.MAXIMUM_SIZE)
.expireAfterAccess(CacheConstants.EXPIRE_TIME, TimeUnit.SECONDS)
.removalListener(new RemovalListener<Long, String>() {
@Override
public void onRemoval(RemovalNotification<Long, String> rn) {
System.out.println(
String.format("[Repository] cache removed: {%d} - {%s}", rn.getKey(), rn.getValue()));
}
})
.build(new CacheLoader<Long, String>() {
@Override
public String load(Long key) {
System.out.println(String.format("[Repository][localCache] get from redis, key = {%d}", key));
return getFromRedisMock(key);
}
});
public String getFromCache(Long id) throws ExecutionException {
System.out.println(String.format("[Repository][getFromCache] get from cache, key = {%d}", id));
// 使用get
return localCache.get(id);
// 使用getIfPresent
// return localCache.getIfPresent(id);
}
public String getFromRedisMock(Long id) {
if (id > 0) {
return "用户" + id;
} else {
return null;
}
}
}
LoadingCache#get
get在获取本地缓存时,如果值不存在,会主动调用上面提到的load方法,如果load成功得到值,那么会将该值保存在本地缓存中。如果load返回了null,则会抛出异常。
测试代码如下
可以看下实验结果,当我们第一次get时,内存里没有对应的值,自动调用load方法去Redis中取值。
如果把userId改成0,那么Redis会返回null,此时get方法会抛出InvalidCacheLoadException异常。
LoadingCache#getIfPresent
接下来测试getIfPresent。把Repository类的getFromCache方法中的get改成getIfPresent后,得到结果如下图。可以发现,getIfPresent在内存中没有对应值的时候,不会去调用load方法,而是直接返回null。