Search This Blog

Tuesday, 25 September 2012

Caching in Spring

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.
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.
How Spring's caching mechanism works
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>
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.

2 comments: