Home > Java > Simple caching with AspectJ

Simple caching with AspectJ

Caching hasn’t been never so easy to implement before. AspectJ is one of the best (probably the best) technologies that can be used for implementing caching system.

There are several ways how to do caching. Object can directly set values to cache, can implement some caching interface or can be fancy and use annotations that sounds to me as a most elegant solution. With annotation you can annotate any method that should be cached. Could be something easier?

Let’s create @Cacheable annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cacheable {

}

With this annotation we can mark any expensive, time consuming methods whose results should be cached. For example let’s create Calculator class that has one very expensive and time consuming method. Every time we call this method with same input it will be executed. It’s a huge wasting of resources. So we will use our @Cacheable annotation on it’s function to prevent executing it when it’s not necessary.

public class Calculator {

	private Logger logger = Logger.getLogger(Calculator.class);

	@Cacheable
	public int sum(int a, int b) {
		logger.info("Calculating " + a + " + " + b);
		try {
			// pretend this is an expensive operation
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			logger.error("Something went wrong...", e);
		}
		return a + b;
	}

}

Next we need to create an aspect called CacheAspect that will handle caching. In this aspect we define pointcut that matches all methods annotated with @Cacheable, regardless of number and type of input arguments and return type:

@Pointcut("execution(@Cacheable * *.*(..))")
private void cache() {}

It is a best practice to annotate empty methods with a pointcut. Function cache() can be considered as a name for a pointcut.
As a last step we need to create an advice – a function that wraps calling of cacheable methods. This function will be actually called every time empty function cache() is invoked. Advise aroundCachedMethods will take a look into cache for a previously cached result and return it. If it couldn’t find anything then it will call our time consuming function and store result for next reuse.

@Around("cache()")
public Object aroundCachedMethods(ProceedingJoinPoint thisJoinPoint)
		throws Throwable {
	logger.debug("Execution of Cacheable method catched");

	// generate the key under which cached value is stored
	// will look like caching.aspectj.Calculator.sum(Integer=1;Integer=2;)
	StringBuilder keyBuff = new StringBuilder();

	// append name of the class
	keyBuff.append(thisJoinPoint.getTarget().getClass().getName());

	// append name of the method
	keyBuff.append(".").append(thisJoinPoint.getSignature().getName());

	keyBuff.append("(");
	// loop through cacheable method arguments
	for (final Object arg : thisJoinPoint.getArgs()) {
		// append argument type and value
		keyBuff.append(arg.getClass().getSimpleName() + "=" + arg + ";");
	}
	keyBuff.append(")");
	String key = keyBuff.toString();

	logger.debug("Key = " + key);
	Object result = cache.get(key);
	if (result == null) {
		logger.debug("Result not yet cached. Must be calculated...");
		result = thisJoinPoint.proceed();
		logger.info("Storing calculated value '" + result + "' to cache");
		cache.put(key, result);
	} else {
		logger.debug("Result '" + result + "' was found in cache");
	}

	return result;
}

Download full source code as a zip or tar.gz file or browse code online on GitHub.

Categories: Java Tags: , , ,
  1. September 12, 2009 at 12:30 PM

    Very good post. I am developing a timing aspect for my application. This has been very useful for me.

    Thank you.

  2. pichulines
    February 19, 2012 at 2:02 AM

    thanks for this…. it is right what I need.

  3. February 20, 2013 at 5:40 AM

    I really speculate the reasons why you labeled this specific
    blog post, “Simple caching with AspectJ
    My web and Java snippets”. Either way I personally
    admired it!Thank you-Winston

  1. July 1, 2013 at 2:06 AM

Leave a comment