In real world applications it’s common to cache some complex operations to get better performance. Caches are used in web service or database calls that aren’t expected to change its result for a long time1. There are some cache libraries, one of the most used in Java is Ehcache. We’ll se how to set up Ehcache with Spring in this post.
A dummy Spring service
Let’s create a dummy service that returns the number of time it’s been called.
public interface MyService { int timesCalled(); }
@Service("myService") public class MyServiceImpl implements MyService { private int count = 0; public int timesCalled() { return ++count; } }
In our main method we call it several times.
System.out.println("Times called: " + myService.timesCalled()); System.out.println("Times called: " + myService.timesCalled()); System.out.println("Times called: " + myService.timesCalled()); System.out.println("Times called: " + myService.timesCalled()); System.out.println("Times called: " + myService.timesCalled());
If we execute it we’ll get the expected output:
Times called: 1
Times called: 2
Times called: 3
Times called: 4
Times called: 5
Set up xml stuff
First of all we configure Spring to use eCache as a cache provider.
<cache:annotation-driven cache-manager="cacheManager" /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcacheManager" /> <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" p:shared="true"/>
In the first line we’re telling Spring that a cache will be used, with a bean called cacheManager
.
In lines 3 to 5 we’re setting up that cache bean by using the Spring eCache manager, with another bean called ecacheManager
.
Finally in lines 7 to 9 we’re creating that bean using a configuration file called ecache.xml
. In this file we’ll set up our cache.
Our ehcache.xml:
<diskStore path="java.io.tmpdir/QuickEhcache" /> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap" /> </defaultCache> <cache name="myCache" eternal="false" maxElementsInMemory="100" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap" /> </cache>
On the first line we’re setting the hard disk directory were the Ehcache will store cached information. In this case we’re using a directory called QuickEhcache
inside the O.S. temporary folder (i.e. in linux /tmp/QuickEhcache
).
Nest we’re setting up two caches: the default cache and a named one. I’ll explain the default cache later. The important things about this configuration are:
timeToIdleSeconds
is the time an element can exist in the cache without being called. The default value is 0, it means no time to iddle, it lives forever.timeToLiveSeconds
is the time an element can exist in the cache regardless of the use. The default time is also 0, it means it lives forever.eternal
. If this option is set to true it’ll override the time to iddle and the time to live. The cache would live forever.maxElementsInMemory
is the number of elements that will be stored in the computer memory before using the hard disk.memoryStoreEvictionPolicy
: Policy would be enforced upon reaching the maxElementsInMemory limit. LRU means Least Recenly Used.persistence
is the persistence strategy to be used.localTempSwap
is used to store elements in hard disk that will be deleted after a restart. Withnone
the hard disk wouldn’t be used.
Caching the service
We have to use the @Cacheable
annotation to tag a method to be cached. We will use it on the implementation class:
@Cacheable(value="myCache") public int timesCalled() { return ++count; }
Now the execution is different:
Times called: 1
Times called: 1
Times called: 1
Times called: 1
Times called: 1
The service is called once, the result is stored by Ehcache and it’s served every time the service is called again. If you set a breakpoint on timesCalled you see that it is reached only once.
In our configuration we have two cache configurations, myCache
and the default one. With this annotation, passing “myCache” on the value
attribute, we’re using the first one. If value
is not set, ehcache will usse the default cache configuration.
You can have more cache configurations. For example, if you want to cache a Web Service and a DAO you probably want different settings, so you can create a wsCache
and a daoCache
configurations on ehcache.xml.
On other post I’ll explain more caching options. You can get a full example on my github repository.
- “A long time” may not mean the same for a computer [↩]
This type of online business elevated quickly over the last few years.