Tags


Everybody loves super fast application and one great way to achieve this is to cache appropriate objects. In data access layer we can cache the business entities that are being referred multiple times in the same session (Hibernate 1st level cache), resultset of frequently used queries . In service layer, caching the result of a method that involves lot of processing can also be done. Spring provides an out of the box solution to achieve this, though it doesn’t provide the actual implementation, the abstraction provided by this API makes it lot easier. This API is only an abstraction and this needs to be backed by store of your choice like JDK’s ConcurrentMap or Ehcache.

Lets take a look at the annotations used here:


public List<Page> findPagesContiainingText(Book hugeBook, String text){

//search all the pages and return the pages with matching text

}

With a logical assumption that the list of pages returned by this above method is not going to change for a given book and text, it is not efficient to do same task(boring & resource intensive) again and again for same question asked different times. So it is time to take your magic wand and mark the method with @Cacheable annotation

@Cacheable(value = “pages”)

public List<Page> findPagesContiainingText(Book hugeBook, String text){

//search all the pages and return the pages if it is not searched before, else return it from cache

}

The above method will cache the results in pages cache using the passed in book and text string as key.

We might add some new pages to the book and this time we have to clear out the old results, here comes the annotation to do that.

@CacheEvict(value = “pages”)

public void addPages(List<Page> pages){

...

}

These are basic simple annotation to enable caching, check here for more : http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/cache.html. These annotation follow the same proxy based implementation so the internal method calls using this reference will not make use of this feature,  the same old catch, want to dig more on this, here it is: http://youtu.be/yiinKulgm4U)

The next step would be to define the backing store. Lets take ehcache based store, it requires the metadata about our “pages” cache to be available in ehcache.xml and it looks like below:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">

<diskStore path="java.io.tmpdir/TrunkBox"/>

<defaultCache
 maxElementsInMemory="10"
 eternal="false"
 timeToIdleSeconds="120"
 timeToLiveSeconds="120"
 overflowToDisk="true"
 maxElementsOnDisk="10000000"
 diskPersistent="false"
 diskExpiryThreadIntervalSeconds="120"
 memoryStoreEvictionPolicy="LRU"
 />

<cache name="pages" eternal="true" clearOnFlush="true"
 maxElementsInMemory="10" overflowToDisk="true" maxElementsOnDisk="10000000"
 timeToIdleSeconds="0"
 memoryStoreEvictionPolicy="LRU" statistics="true"/>

</ehcache>

Now the next job is to let the spring framework know that we want the caching feature to be enabled(see the @EnableCaching in below example) and also the details about the store (ehcache in our case) that we have planned to use. This configuration should have the details about the following beans – EhCacheManagerFactoryBean, CacheManager, EhCacheFactoryBean.

@Configuration
@EnableCaching

public class CacheConfig {
 @Bean
 public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
 EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
 ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml", getClass()));
 return ehCacheManagerFactoryBean;
 }

@Bean
 public CacheManager cacheManager() {
 EhCacheCacheManager cacheManager = new EhCacheCacheManager();
 cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());

return cacheManager;

}

@Bean
 public EhCacheFactoryBean ehCacheFactory() {
 EhCacheFactoryBean ehCacheFactory = new EhCacheFactoryBean();
 ehCacheFactory.setCacheManager(ehCacheManagerFactoryBean().getObject());
 return ehCacheFactory;
 }
}

Lot many times we may want to load some items in to the cache during startup, ehache provides option for that with the help of BootstrapCacheLoaderFactory class and BootstrapCacheLoader interface. We need to add our own cache loader factory extending and implementing the above mentioned class & interface as show below.

public class MyBootstrapCacheLoaderFactory extends BootstrapCacheLoaderFactory
 implements BootstrapCacheLoader

{
 public MyBootstrapCacheLoaderFactory() {
 super();
 // TODO Auto-generated constructor stub
 }

@Override
 public BootstrapCacheLoader createBootstrapCacheLoader(Properties properties) {
 // TODO Auto-generated method stub

return new MyBootstrapCacheLoaderFactory();
 }
 public Object clone() throws CloneNotSupportedException {
 // TODO Auto-generated method stub
 return super.clone();
 }

public boolean isAsynchronous() {
 // TODO Auto-generated method stub
 return false;
 }

public void load(Ehcache myCache) throws CacheException {

try {
 //place calls to the method that need to be cached – myService.findPagesWithText(bkReadByAll, textSearchedByAll)

 } catch (Exception e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

 }
}

Then we need to publish this bootstrap loader factory bean and also let the EhCacheFactoryBean know that we have a custom bootstrap cache loader by setting a property in cache configuration as shown below:

@Bean
 public MyBootstrapCacheLoaderFactory myBootstrapCacheLoaderFactory() {
 return new MyBootstrapCacheLoaderFactory();
 }

…

ehCacheFactory.setBootstrapCacheLoader(myBootstrapCacheLoaderFactory());

Coffee served hot and the application served fast will always taste good :p Hope we will be able to make our coffee lit bit hotter with caching !