Saturday, February 20, 2010

EHCache with Spring AOP

Building on the previous post (Spring AOP with @AspectJ annotations), we now add EHCache functionality to the Aspect class.  We won't be adding any new Java class files, but we'll be modifying the existing class files.  Here's a screenshot of the finished project in Eclipse.  New elements are highlighted in red.

Adding EHCache functionality

Add the EHCache JAR file to the project.  We'll be using ehcache-core-1.7.0.jar.  Grab your copy from  You'll also need to create configuration file for EHCache.  Name it ehcache.xml and put it under the com.aop package.  Here's the content of the file:

<ehcache xmlns:xsi="" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false">

    <diskStore path=""/>


    <cache name="defaultCache"

Next, we'll need to configure EHCache to be available in Spring.  For this, the app-ctx.xml needs to be edited.  Add the following lines to the app-ctx.xml file:

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    <property name="configLocation" value="classpath:com/aop/ehcache.xml"></property>

<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager">
        <ref local="cacheManager" />
    <property name="cacheName" value="defaultCache"/>

The Caching Aspect Class

Now that Spring and EHCache have been configured, we can modify the class file to include the caching features.  We begin by changing the PointCut in the  Instead of intercepting all public methods, we now only intercept public methods beginning with getMap.  In addition, we a private Cache property to hold the method cache as well as some logic to store/retrieve the method cache.  Here's the updated class:

package com.aop;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

public class MyAspect {
    private Cache methodCache;
    @Around("execution(public * getMap*(..))")
    public Object executor(ProceedingJoinPoint point) throws Throwable {

        System.out.println("Cutting before...");
        Object retVal;
        String methodName = point.getTarget().getClass().toString() + "," + point.toShortString();

        if (methodCache.isKeyInCache(methodName)) {
            System.out.println("Returning from cache....");
            return methodCache.get(methodName).getObjectValue();
        } else {
            retVal = point.proceed();
            if (retVal != null) {
                System.out.println("returned object: " + retVal.toString());
                System.out.println("Storing into cache...");
                methodCache.put(new Element(methodName, retVal));

        System.out.println("Cutting after...");
        return retVal;

    public Cache getMethodCache() {
        return methodCache;

    public void setMethodCache(Cache methodCache) {
        this.methodCache = methodCache;
As the cache key, we're using a combination of the class name and the method name.  The rest of the changes are pretty straight forward. 

Spring Managed Bean -

We add a new method called getMap1() in  Here's the code:

public HashMap<String, String> getMap1() {
    System.out.println("getMap1 == init");
    HashMap<String, String> map = new HashMap<String, String>();
    for (int i = 0; i < 100; i++) {
        map.put(Integer.toString(i), "new value " + i);

    System.out.println("getMap1 == return");
    return map;

Main Runner

In the class which contains the main method, we'll be calling the getMap1() multiple times to determine if the caching is working.  The output should be like below:

Cutting before...
getMap1 == init
getMap1 == return
returned object: {35=new value 35, 36=new value 36, 33=new value 33, 34=new value 34, 39=new value 39, 37=new value 37, 38=new value 38, 43=new value 43, 42=new value 42, 41=new va
lue 41, 40=new value 40, 22=new value 22, 23=new value 23, 24=new value 24, 25=new value 25, 26=new value 26, 27=new value 27, 28=new value 28, 29=new value 29, 3=new value 3, 2=ne
w value 2, 1=new value 1, 0=new value 0, 30=new value 30, 7=new value 7, 6=new value 6, 32=new value 32, 5=new value 5, 31=new value 31, 4=new value 4, 9=new value 9, 8=new value 8
, 19=new value 19, 17=new value 17, 18=new value 18, 15=new value 15, 16=new value 16, 13=new value 13, 14=new value 14, 11=new value 11, 12=new value 12, 21=new value 21, 20=new v
alue 20, 99=new value 99, 98=new value 98, 97=new value 97, 96=new value 96, 95=new value 95, 94=new value 94, 93=new value 93, 92=new value 92, 91=new value 91, 90=new value 90, 1
0=new value 10, 88=new value 88, 89=new value 89, 79=new value 79, 78=new value 78, 77=new value 77, 82=new value 82, 83=new value 83, 80=new value 80, 81=new value 81, 86=new valu
e 86, 87=new value 87, 84=new value 84, 85=new value 85, 67=new value 67, 66=new value 66, 69=new value 69, 68=new value 68, 70=new value 70, 71=new value 71, 72=new value 72, 73=n
ew value 73, 74=new value 74, 75=new value 75, 76=new value 76, 59=new value 59, 58=new value 58, 57=new value 57, 56=new value 56, 55=new value 55, 64=new value 64, 65=new value 6
5, 62=new value 62, 63=new value 63, 60=new value 60, 61=new value 61, 49=new value 49, 48=new value 48, 45=new value 45, 44=new value 44, 47=new value 47, 46=new value 46, 51=new
value 51, 52=new value 52, 53=new value 53, 54=new value 54, 50=new value 50}
Storing into cache...
Cutting after...
Cutting before...
Returning from cache....
Cutting before...
Returning from cache....
Cutting before...
Returning from cache....

No comments: