Google이 만든 Guava Library엔 이러한 캐시 기능이 포함되어 있다. Guava의 Cache는 키-값 형태의 데이터 구조로 표현되며 손쉽게 캐시의 크기, 리프레시, 로딩 방법 등을 지정할 수 있다. Guava Cache는 내부적으로 ConcurrentHashMap과 유사하게 구현되어 있다고.. 따라서 동시성 문제를 덜 걱정해도 된다. (thread-safe)
Guava에서는 LoadingCache와 Cache의 2가지 타입의 캐시를 제공하는데 필요한 값이 없을 때 데이터를 다시 로드해주는 LoadingCache가 주로 쓰인다. LoadingCache는 CacheBuilder를 이용해 생성하는데 build() 호출 시 load()나 loadAll()을 오버라이드한 CacheLoader 구현체를 파라미터로 전달해 주어야 한다. 캐시 미스가 발생하면 알아서 로딩 메서드가 호출되는 방식이다. (get -> load, getAll -> loadAll)
Guava Cache는 캐시의 최대 사이즈나 시간을 기준으로 캐시가 적정 사이즈를 유지할 수 있도록 지원한다. 사이즈에 대한 설정도 마음에 들지만 expireAfterAccess나 expireAfterWrite는 특히 유용해 보인다. After Access는 마지막 참조 이후 설정된 시간이 지나면 캐시에서 삭제되고, After Write는 마지막 갱신(load) 이후 설정된 시간이 지나면 캐시에서 삭제된다. 그리고 removalListener()에 리스너를 등록해서 삭제되는 데이터를 확인할 수 있고, CacheStats로 hitRate 등을 측정할 수도 있다.
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(MY_LISTENER) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) { return createExpensiveGraph(key); } });
데이터를 가져오는 API가 조금씩 다른긴 하지만 Cache 인스턴스를 만든 뒤엔 Map 쓰듯이 쓰면 된다.
https://github.com/google/guava/wiki/CachesExplained