Here's an example :
ehCache.xml :
<ehcache>
<defaultCache
maxElementsInMemory="500"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" />
<cache name="getTestCache"
maxElementsInMemory="50"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
applicationContext.xml :
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springmodules.org/schema/ehcache
http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">
<ehcache:config configLocation="classpath:ehcache.xml" />
<ehcache:annotations>
<ehcache:caching id="getTestCacheModel" cacheName="getTestCache" />
<ehcache:flushing id="getTestFlushModel" cacheNames="getTestCache" />
</ehcache:annotations>
<bean id="customerManager" class="services.impl.CustomerManagerImpl"/>
</beans>
The CustomerManager interface :
import org.springmodules.cache.annotations.Cacheable;
import org.springmodules.cache.annotations.CacheFlush;
import vo.Customer;
public interface CustomerManager {
@Cacheable(modelId="getTestCacheModel")
public Customer load(long customerId);
@CacheFlush(modelId="getTestFlushModel")
public void add(Customer customer);
}
In order to do a simple test, we create a simple implementation of the CustomerManager interface :
import services.CustomerManager;
import vo.Customer;
public class CustomerManagerImpl implements CustomerManager {
public Customer load(long customerId) {
//This part should normally call a DAO
return new Customer("Rene", 34);
}
public void add(Customer customer) {
//This part should normally call a DAO
}
}
And the test class :
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import services.CustomerManager;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerManager customerManager = (CustomerManager) context.getBean("customerManager");
System.err.println("Loading customer");
customerManager.load(455L);
System.err.println("Loading customer again");
customerManager.load(455L);
System.err.println("Adding customer");
customerManager.add(new Customer("Jean",34));
}
}
The final step is to add this simple log4j.properties file to the classpath :
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=DEBUG, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
Here's the log result of the execution of this class :
Loading customer
- Attempt to retrieve a cache entry using key <1187678266|32099260> and cache model
- Retrieved cache element
- Attempt to store the objectin the cache using key <1187678266|32099260> and model
- Object was successfully stored in the cache
Loading customer again
- Attempt to retrieve a cache entry using key <1187678266|32099260> and cache model
- Retrieved cache element
Adding customer
- Attempt to flush the cache using model
- Cache has been flushed.
We can see that the first time we call the load method, the retrieved Customer object is stored in the cache. The second time, the object is directly retrieved from the cache. When we call the add method the cache is flushed.
Here's the maven pom file I used :
We'll use the 0.8 version of springmodules, the latest in the maven repo central. This version has dependencies on spring 2.0 and ehcahe 1.1. In order to use the latest library we will add our own version of spring (2.5.6) and ehcache (1.6.2) and excludes the old ones. We have to exclude also all the proprietary libraries which should be optional but are not.
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.8</version>
<exclusions>
<exclusion>
<artifactId>gigaspaces-ce</artifactId>
<groupId>gigaspaces</groupId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>jsk-lib</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>jsk-platform</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>mahalo</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>reggie</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>start</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>boot</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>webster</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-cache</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-common</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-jmx</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-minimal</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-system</artifactId>
</exclusion>
<exclusion>
<groupId>jcs</groupId>
<artifactId>jcs</artifactId>
</exclusion>
<exclusion>
<groupId>xpp3</groupId>
<artifactId>xpp3_min</artifactId>
</exclusion>
<exclusion>
<groupId>ehcache</groupId>
<artifactId>ehcache</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
In conclusion, annotation driven Caching with EhCache and Spring is very straightforward to implement.
However, be aware that the springmodules framework is in a dead status and have some unresolved issues you should be aware of. You can see those issues listed here. Take a look at it before using it though most of the issues have workarounds. There is also a 0.9 version you can only download because it has never been released in the maven central repository.
A project named Spring modules fork has been created to revive this useful project and to make it evoluate. They've created a 0.10-SNAPSHOT version hosted on their own server.
reaaly nice
ReplyDeleteNice post. The last time I checked, Spring Modules was still in Beta and I was little hesitant to use it in production.
ReplyDeleteHere is another way to do the integration: http://blog.inflinx.com/?p=121
Why not just Ehcache DX management / tuning tools at http://ehcache.org? Designed by the core ehcache team.
ReplyDeleteFull disclosure: I work on ehcache at terracotta.
Cheers,
--Ari
I like using annotations to configure my caching strategy. It's clear and simple. I didn't find any way to do this with ehcache only.
ReplyDeleteI am one of the authors of a new project intended to provide Ehcache integration for Spring 3 projects via annotations:
ReplyDeletehttp://code.google.com/p/ehcache-spring-annotations/
We are excited to announce the general availability of the first production release, 1.0.1.
This release provides 2 method-level annotations in the spirit of Spring’s @Transactional:
@Cacheable
@TriggersRemove
When appropriately configured in your Spring application, this project will create caching aspects at runtime around your @Cacheable annotated methods.
Usage documentation can be found on the project wiki:
http://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheAnnotations
http://code.google.com/p/ehcache-spring-annotations/wiki/UsingTriggersRemove
Thanks. I will definitively take a look at your project.
ReplyDeleteIt also addresses the issues with consistent key generation that you address in another blog post.
ReplyDeleteFor some reasons application context did not come in previous post. Please have it below.
ReplyDeleteThanks,
Pawan
Application Context . xml
=========================
Be aware that Spring Modules and Spring Modules Fork are currently not compatible with spring 3.0. If you really want to upgrade to spring 3.0, use the solution described above by Eric.
ReplyDeletei am trying to work with Spring 3.1 m1 cache abstraction -
ReplyDeletebut, getting this ERROR :
Error creating bean with name 'org.springframework.cache.interceptor.CacheInterceptor#0'
Please help me out!!
great post; this means i can prob get out of here before midnight. if you were in the office, i'd shout the beers. thanks!
ReplyDelete