Many a times we come upon the need for using caching in our code to improve performance. In my very first IT project we had done caching by writing our own code involving maps, generics and some get put calls. Now we know about readily available third party cache providers and Spring fortunately provides integration for several of these.
For my first stab at caching I decided to use an implementation provided by using EhCache. I did not use the annotation driven technique for this post. For this example I created a simple class that generates the data.
In this case our method cached is getStudentName and the cache used is serviceCache. The cache details are configured in the xml file:
There is the case where we may need to clear the cache when an updateStudent or removeStudent method is called.
All this seems a lot of configurations. We can adopt Spring's newer style of annotations for cache. It reduces the XML and also hides the proxy complexity from us.
public class StudentService { private static Map<Integer, String> students = new LinkedHashMap<Integer, String>(); static { students.put(1, "Robin"); students.put(2, "Reena"); students.put(3, "Ramesh"); students.put(4, "Virat"); students.put(5, "Neeta"); } public String getStudentName(final int id) { System.out.println("Request received at target bean " + id); return students.get(id); } }Now the time to implement caching. The advantage of spring's caching mechanism is that we do not need to add any details in our code:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:ehc="http://www.springmodules.org/schema/ehcache" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-3.1.xsd"> <bean id="serviceBean" class="com.service.StudentService" /> <!-- It defines the advise for the cache based aspect --> <cache:advice id="cacheProvider" cache-manager="cacheManager" /> <!-- The cache Manager to use - Ehcache --> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhcacheCacheManager" p:cache-manager="ehcache" /> <!-- The path for the ehcache configuration file --> <ehc:config configLocation="classpath:ehcache.xml" /> <ehc:proxy id="service" refId="serviceBean"> <!-- The method to cache in the service class and the cache to use --> <ehc:caching methodName="getStudentName" cacheName="serviceCache" /> </ehc:proxy> </beans>As can be seen from the XML file, Spring creates proxies that manage the caching behaviour for our services.
In this case our method cached is getStudentName and the cache used is serviceCache. The cache details are configured in the xml file:
<ehcache> <diskStore path="java.io.tmpdir" /> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <cache name="serviceCache" maxElementsInMemory="100" timeToIdleSeconds="120" timeToLiveSeconds="720" overflowToDisk="true" /> </ehcache>I created some sample code to test the caching:
public static void main(String[] args) { final ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-cache.xml"); final StudentService service = (StudentService) applicationContext .getBean("service"); service.getStudentName(1); service.getStudentName(2); service.getStudentName(1); }The output logs indicates that the third call was restricted to the cache and never reached the service bean.
1015 [main] DEBUG net.sf.ehcache.store.DiskStore - Deleting data file
serviceCache.data
1093 [main] DEBUG net.sf.ehcache.store.MemoryStore - Initialized net.sf
.ehcache.store.LruMemoryStore for serviceCache
1093 [main] DEBUG net.sf.ehcache.store.LruMemoryStore - serviceCache Cache: Using
SpoolingLinkedHashMap implementation
1093 [main] DEBUG net.sf.ehcache.Cache - Initialised cache: serviceCache
...
2218 [main] DEBUG org.springmodules.cache.provider.ehcache.EhCacheFacade - Attempt
to retrieve a cache entry using key <453088645|12245162> and
cache model <org.springmodules.cache.provider.ehcache. EhCacheCachingModel@32784a[cacheName='serviceCache']> 2218 [main] DEBUG net.sf.ehcache.store.MemoryStore - serviceCacheCache: serviceCacheMemoryStore miss for 453088645|12245162 2218 [main] DEBUG net.sf.ehcache.Cache - serviceCache cache - Miss Request received at target bean 1 2297 [main] DEBUG org.springmodules.cache.provider.ehcache.EhCacheFacade - Attempt to store the object <Robin> in the cache using key <453088645|12245162> and model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@32784a[cacheName='serviceCache']> 2297 [main] DEBUG net.sf.ehcache.store.MemoryStore - serviceCacheCache: spool to disk done for: 453088645|12245162 2297 [main] DEBUG org.springmodules.cache.provider.ehcache.EhCacheFacade - Object was successfully stored in the cache ... Request received at target bean 2 ... 2297 [main] DEBUG org.springmodules.cache.provider.ehcache.EhCacheFacade - Retrieved cache element <'Robin'>The first two records weren't found and then they were added to the cache. on the third Request the entry was detected in the cache and the call therefore was not made to the Service bean.
There is the case where we may need to clear the cache when an updateStudent or removeStudent method is called.
<ehc:proxy id="service" refId="serviceBean"> <ehc:flushing cacheNames="serviceCache" methodName="---name of method---"/> <ehc:caching methodName="getStudentName" cacheName="serviceCache" /> </ehc:proxy>
Really good and nice feature.
ReplyDelete