My objective here is to cover one of the newer cache implementations that Spring now provides with 4.0+ version of the framework - using Google Guava Cache
In brief, consider a service which has a few slow methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class DummyBookService implements BookService { @Override public Book loadBook(String isbn) { // Slow method 1. } @Override public List<Book> loadBookByAuthor(String author) { // Slow method 2 } } |
With Spring Caching abstraction, repeated calls with the same parameter can be sped up by an annotation on the method along these lines - here the result of loadBook is being cached in to a "book" cache and listing of books cached into another "books" cache:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class DummyBookService implements BookService { @Override @Cacheable ( "book" ) public Book loadBook(String isbn) { // slow response time.. } @Override @Cacheable ( "books" ) public List<Book> loadBookByAuthor(String author) { // Slow listing } } |
Now, Caching abstraction support requires a CacheManager to be available which is responsible for managing the underlying caches to store the cached results, with the new Guava Cache support the CacheManager is along these lines:
1 2 3 4 | @Bean public CacheManager cacheManager() { return new GuavaCacheManager( "books" , "book" ); } |
Google Guava Cache provides a rich API to be able to pre-load the cache, set eviction duration based on last access or created time, set the size of the cache etc, if the cache is to be customized then a guava CacheBuilder can be passed to the CacheManager for this customization:
1 2 3 4 5 6 | @Bean public CacheManager cacheManager() { GuavaCacheManager guavaCacheManager = new GuavaCacheManager(); guavaCacheManager.setCacheBuilder(CacheBuilder.newBuilder().expireAfterAccess( 30 , TimeUnit.MINUTES)); return guavaCacheManager; } |
This works well if all the caches have a similar configuration, what if the caches need to be configured differently - for eg. in the sample above, I may want the "book" cache to never expire but the "books" cache to have an expiration of 30 mins, then the GuavaCacheManager abstraction does not work well, instead a better solution is actually to use a SimpleCacheManager which provides a more direct way to get to the cache and can be configured this way:
1 2 3 4 5 6 7 8 9 10 | @Bean public CacheManager cacheManager() { SimpleCacheManager simpleCacheManager = new SimpleCacheManager(); GuavaCache cache1 = new GuavaCache( "book" , CacheBuilder.newBuilder().build()); GuavaCache cache2 = new GuavaCache( "books" , CacheBuilder.newBuilder() .expireAfterAccess( 30 , TimeUnit.MINUTES) .build()); simpleCacheManager.setCaches(Arrays.asList(cache1, cache2)); return simpleCacheManager; } |
This approach works very nicely, if required certain caches can be configured to be backed by a different caching engines itself, say a simple hashmap, some by Guava or EhCache some by distributed caches like Gemfire.