Transparent caching with AOP
Often, the objects that compose applications perform the same operations with
the same arguments and obtain the same results. Sometimes, these operations are
costly in terms of CPU usage, or may be there is a lot of I/O going on while
executing those operations.
To get better results in terms of speed and resources used, it's suggested to
use a cache. We can store in it the results corresponding to the methods'
invocations as key-value pairs: method and arguments as key and return object
as value.
Once you decide to use a cache you're just halfway. In fact, you must decide
which part of the application is going to use the cache. Let's think about a
web application backed by a database. Such a web application usually involves
Data Access Objects (DAOs), which access the relational database. Such objects
are usually a bottleneck in the application as there is a lot of I/O going on.
In other words, a cache can be used there.
The cache can also be used by the business layer that has already aggregated and
elaborated data retrieved from repositories, or it can be used by the
presentation layer putting formatted presentation templates in the cache, or
even by the authentication system that keeps roles according to an
authenticated username.
There are almost no limits as to how you can optimize an application and make it
faster. The only price you pay is having RAM to dedicate the objects that are
to be kept in memory, besides paying attention to the rules on how to manage
the life of the objects in cache.
After these preliminary remarks, using a cache could seem common and obvious. A
cache essentially acts as a hash into which key-value pairs are put. The keys
are used to retrieve objects from the cache. Caching usually has confi guration
parameters that allow you to change its behavior.
Now let's have a look at an example with ehcache (http://ehcache.sourceforge.
net). First of all let's confi gure it with the name methodCache so that we
have at the most 1000 objects. The objects are inactive for a maximum of fi ve
minutes, with a maximum life of 10 minutes. If the objects count is over 1000,
ehcache saves them on the fi lesystem, in java.io.tmpdir.
<ehcache>
…
<diskStore path="java.io.tmpdir"/>
….
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
…
<cache name="methodCache"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
/>
</ehcache>
Now let's create a CacheAspect. Let's defi ne the cacheObject to which the
ProceedingJoinPoint is passed. Let's recover an unambiguous key from the
ProceedingJoinPoint with the method getCacheKey. We will use this key to put
the objects into the cache and to recover them.
Once we have obtained the key, we ask to cache the Element with the instruction
cache.get(cacheKey). The Element has to be evaluated because it may be null if
the cache didn't fi nd an Element with the passed cacheKey.
If the Element is null, advice invokes the method proceed(), and puts in the
cache the Element with the key corresponding to the invocation. Otherwise, if
the Element recovered from the cache is not null, the method isn't invoked on
the target class, and the value taken from the cache is returned to the caller.
package org.springaop.chapter.five.cache;
import it.springaop.utils.Constants;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
public class CacheAspect {
public Object cacheObject(ProceedingJoinPoint pjp) throws Throwable
{
Object result;
String cacheKey = getCacheKey(pjp);
Element element = (Element) cache.get(cacheKey);
logger.info(new StringBuilder("CacheAspect
invoke:").append("\n get:") .append(cacheKey).append("
value:").append(element). toString());
if (element == null) {
result =
pjp.proceed();
element = new
Element(cacheKey, result);
cache.put(element);
logger.info(new StringBuilder("\n put:").append(cacheKey). append( "
value:").append(result).toString());
}
return element.getValue();
}
public void flush() {
cache.flush();
}
private String getCacheKey(ProceedingJoinPoint pjp) {
String targetName =
pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
Object[] arguments = pjp.getArgs();
StringBuilder sb = new StringBuilder();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) &&
(arguments.length != 0)) {
for (int i = 0; i <
arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
public void setCache(Cache cache) {
this.cache = cache;
}
private Cache cache;
private Logger logger =
Logger.getLogger(Constants.LOG_NAME);
}
Here is applicationContext.xml :
<beans xmlns=»http://www.springframework.org/schema/beans»
xmlns:xsi=»http://www.w3.org/2001/XMLSchema-instance»
xmlns:aop=»http://www.springframework.org/schema/aop»
xsi:schemaLocation=»http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd»> …
<bean id="rockerCacheAspect" class="org.springaop.chapter.five.
cache.CacheAspect" >
<property name="cache">
<bean id="bandCache" parent="cache">
<property
name="cacheName" value="methodCache" />
</bean>
</property>
</bean>
<!-- CACHE config -->
<bean id="cache" abstract="true"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager" />
</bean>,
<bean id="cacheManager"
class="org.springframework.cache.ehcache.
EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:org/springaop/
chapter/five/cache/ehcache.xml" />
</bean>
…
</beans>
The idea about the caching aspect is to avoid repetition in our code base and
have a consistent strategy for identifying objects (for example using the hash
code of an object) so as to prevent objects from ending up in the cache twice.
Employing an around advice, we can use the cache to make the method invocations
return the cached result of a previous invocation of the same method in a
totally transparent way. In fact, to the methods of the classes defi ned in the
interception rules in pointcuts will be given back the return values drawn from
the cache or, if these are not present, they will be invoked and inserted in
the cache. In this way, the classes and methods don't have any knowledge of
obtaining values retrieved from the cache.
Also read
Explain the concepts and capabilities of Aspect-Oriented Programming, AOP.
What is Aspect in AOP?
AOP approach addresses Crosscutting concerns. Explain
The components of AOP are advices/interceptors, introductions, metadata, and
pointcuts. Explain them
AOP vs OOPs...........
What is the relation between Classes and Objects? Explain different properties
of Object Oriented Systems. What is difference between Association, Aggregation
and Inheritance relationships? Explain the features of an abstract class in
NET. Difference between abstract classes and interfaces Similarities and
difference between Class and structure in .NET Features of Static/Shared
classes. What is Operator Overloading in .NET?.............
What
is object oriented programming (OOP)?
The object oriented programming is commonly known as OOP. Most of the languages
are developed using OOP concept. Object-oriented programming (OOP) is a
programming concept that uses "objects" to develop a system.........
What
are the various elements of OOP?
Various elements of OOP are.........
Explain
an object, class and Method.
An object is an entity that keeps together state and behaviors.
For instance, a car encapsulates state such as red color, 900 cc etc and
behaviors as 'Start', 'Stop' etc., so does an object...............
|