测试的时候发现项目中的LoadingCache没有刷新,但是明明调用了refresh方法了。后来发现LoadingCache是不支持缓存null值的,如果load回调方法返回null,则在get的时候会抛出异常。
通过几个例子开看这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public void test_loadNull() { LoadingCache<String, String> stringCache = CacheBuilder.newBuilder() .maximumSize(10) .build(new CacheLoader<String, String>() { @Override public String load(String s) throws Exception { System.out.println("xx"); if (s.equals("hello")) return "world"; else return null; } });
try { System.out.println(stringCache.get("hello"));
System.out.println(stringCache.get("other_key")); } catch (ExecutionException e) { e.printStackTrace(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public void test_loadNullWhenRefresh() { LoadingCache<String, String> stringCache = CacheBuilder.newBuilder() .maximumSize(10) .build(new CacheLoader<String, String>() { int i = 0;
@Override public String load(String s) throws Exception { if (i == 0) { i++; return "world"; } return null; } });
try { System.out.println(stringCache.get("hello")); System.out.println(stringCache.get("hello"));
stringCache.refresh("hello");
System.out.println(stringCache.get("hello")); } catch (ExecutionException e) { e.printStackTrace(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public void test_loadNullAfterInvalidate() { LoadingCache<String, String> stringCache = CacheBuilder.newBuilder() .maximumSize(10) .build(new CacheLoader<String, String>() { int i = 0;
@Override public String load(String s) throws Exception { if (i == 0) { i++; return "world"; } return null; } });
try { System.out.println(stringCache.get("hello")); System.out.println(stringCache.get("hello"));
stringCache.invalidate("hello");
System.out.println(stringCache.get("hello")); } catch (ExecutionException e) { e.printStackTrace(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void test_loadThrowException() { LoadingCache<String, String> stringCache = CacheBuilder.newBuilder() .maximumSize(10) .build(new CacheLoader<String, String>() { @Override public String load(String s) throws Exception { if (s.equals("hello")) return "world"; else throw new IllegalArgumentException("only_hello"); } });
try { System.out.println(stringCache.get("hello"));
System.out.println(stringCache.get("other_key")); } catch (ExecutionException e) { e.printStackTrace(); } }
|
所以如果你需要缓存“空”值,推荐的做法是使用Optional对象来封装结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public void test_loadUseOptional() { LoadingCache<String, Optional<String>> stringCache = CacheBuilder.newBuilder() .maximumSize(10) .build(new CacheLoader<String, Optional<String>>() { @Override public Optional<String> load(String s) throws Exception { if (s.equals("hello")) return Optional.of("world"); else return Optional.absent(); } });
try { Optional<String> hello = stringCache.get("hello"); if(hello.isPresent()) { System.out.println(hello.get()); }
Optional<String> otherKey = stringCache.get("other_key"); if(otherKey.isPresent()){ System.out.println(otherKey.get()); } } catch (ExecutionException e) { e.printStackTrace(); } }
|
如果你的场景中认为null是不存在的,那么你可以在load函数中抛出异常,这个异常会通过get抛出。
另外还有一个问题,如果是key==null呢?答案是直接抛出java.lang.NullPointerException
。Guava对于null是很不待见的。
参考资料