Ehcache is a nice way to cache method responses. This is nice for example if one of your methods calls a web service or takes very long to execute.
I used ehcache with Spring, so I had to do some extra stuff so the annotations would work.
First I added ehcache and google's ehcache annotations for spring to my pom
I then placed ehcache.xml in my classpath (Putting it in WEB-INF didnt work)
My ehcache.xml looked like this:
By setting the eternal value to true, this cache never expires unless I specifically trigger remove (see below). There are other ways to have the cache expire on a time limit. Please see the ehcache docs for this.
Then I created a Cache class that returned the Collection object that I wanted to cache:
In my Spring configuration, I annotated this Cache class as a Service. This means Spring took care of initializing this class for me as a singleton on startup, so I could just autowire it and call the methods when needed.
Here is my jUnit file:
I used ehcache with Spring, so I had to do some extra stuff so the annotations would work.
First I added ehcache and google's ehcache annotations for spring to my pom
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.googlecode.ehcache-spring-annotations</groupId>
<artifactId>ehcache-spring-annotations</artifactId>
<version>1.2.0</version>
</dependency>
Then in my Spring context file I added:
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.googlecode.ehcache-spring-annotations</groupId>
<artifactId>ehcache-spring-annotations</artifactId>
<version>1.2.0</version>
</dependency>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
<ehcache:annotation-driven cache-manager="ehCacheManager"/>
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
This allows us to use the @Cacheable and @TriggersRemove annotations that come from google.xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
<ehcache:annotation-driven cache-manager="ehCacheManager"/>
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
I then placed ehcache.xml in my classpath (Putting it in WEB-INF didnt work)
My ehcache.xml looked like this:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir/ClavisCaches"/>
<cache name="personCache" maxElementsInMemory="9000" eternal="true" overflowToDisk="false"/>
</ehcache>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir/ClavisCaches"/>
<cache name="personCache" maxElementsInMemory="9000" eternal="true" overflowToDisk="false"/>
</ehcache>
By setting the eternal value to true, this cache never expires unless I specifically trigger remove (see below). There are other ways to have the cache expire on a time limit. Please see the ehcache docs for this.
Then I created a Cache class that returned the Collection object that I wanted to cache:
import com.googlecode.ehcache.annotations.Cacheable;
import com.googlecode.ehcache.annotations.TriggersRemove;
...
@Cacheable(cacheName = "personCache")
public Collection<CacheElement> getPersonCacheSet() {
List<Person> personList = wsClient.getSomeExpensiveCall();
Collection<CacheElement> personCacheSet = new HashSet<CacheElement>(personList.size());
for (Person person : personList) {
CacheElement cacheElement = new CacheElement();
cacheElement.setName(person.getName());
cacheElement.setMobil(person.getMobil());
personCacheSet.add(cacheElement);
}
return personCacheSet;
}
@TriggersRemove(cacheName = "personCache", when = When.AFTER_METHOD_INVOCATION, removeAll = true)
public void clearPersonCache() {
// Intentionally blank
}
import com.googlecode.ehcache.annotations.TriggersRemove;
...
@Cacheable(cacheName = "personCache")
public Collection<CacheElement> getPersonCacheSet() {
List<Person> personList = wsClient.getSomeExpensiveCall();
Collection<CacheElement> personCacheSet = new HashSet<CacheElement>(personList.size());
for (Person person : personList) {
CacheElement cacheElement = new CacheElement();
cacheElement.setName(person.getName());
cacheElement.setMobil(person.getMobil());
personCacheSet.add(cacheElement);
}
return personCacheSet;
}
@TriggersRemove(cacheName = "personCache", when = When.AFTER_METHOD_INVOCATION, removeAll = true)
public void clearPersonCache() {
// Intentionally blank
}
Note that the cacheable methods need to be called from a different class, because of the way the proxy is set up.
In my Spring configuration, I annotated this Cache class as a Service. This means Spring took care of initializing this class for me as a singleton on startup, so I could just autowire it and call the methods when needed.
Here is my jUnit file:
@Autowired
private PersonCache personCache;
@Test
public void testGetPersonCacheSet() {
// The first call makes the expensive call, the second call gets the result from the cache
Collection<CacheElement> personCacheSet0 = personCache.getPersonCacheSet();
Collection<CacheElement> personCacheSet1 = personCache.getPersonCacheSet();
assertSame(dealerCacheSet0, dealerCacheSet1);
}
@Test
public void testClearCache() {
// fill the cache
personCache.getPersonCacheSet();
// The first call gets the result from the cache
Collection<PersonCacheElement> personCacheSet0 = personCache.getPersonCacheSet();
personCache.clearPersonCache();
// second call makes the expensive call
Collection<CacheElement> personCacheSet1 = personCache.getPersonCacheSet();
assertNotSame(personCacheSet0, personCacheSet1);
}
private PersonCache personCache;
@Test
public void testGetPersonCacheSet() {
// The first call makes the expensive call, the second call gets the result from the cache
Collection<CacheElement> personCacheSet0 = personCache.getPersonCacheSet();
Collection<CacheElement> personCacheSet1 = personCache.getPersonCacheSet();
assertSame(dealerCacheSet0, dealerCacheSet1);
}
@Test
public void testClearCache() {
// fill the cache
personCache.getPersonCacheSet();
// The first call gets the result from the cache
Collection<PersonCacheElement> personCacheSet0 = personCache.getPersonCacheSet();
personCache.clearPersonCache();
// second call makes the expensive call
Collection<CacheElement> personCacheSet1 = personCache.getPersonCacheSet();
assertNotSame(personCacheSet0, personCacheSet1);
}
No comments:
Post a Comment