Saturday, August 13, 2011

Simple Introduction to AOP - Session 2

Here, I will show how the cross-cutting concern that was introduced in the previous session, can be implemented using Spring AOP - Spring offers multiple ways of implementing Aspects - XML configuration based, @AspectJ based. In this specific example, I will use XML configuration file based way of defining the aspect

Spring AOP works in the context of a Spring container, so the service implementation that was defined in the previous session needs to be a Spring bean, I am defining it using the @Service annotation:
@Service
public class DefaultInventoryService implements InventoryService{
...
}
Now, I want to record the time taken for each of the method calls of my DefaultInventoryService - I am first going to modularize this as an "advice":
package org.bk.inventory.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AuditAdvice {

    private static Logger logger = LoggerFactory.getLogger(AuditAdvice.class);

    public void beforeMethod() {
        logger.info("before method");
    }

    public void afterMethod() {
        logger.info("after method");
    }

    public Object aroundMethod(ProceedingJoinPoint joinpoint) {
        try {
            long start = System.nanoTime();
            Object result = joinpoint.proceed();
            long end = System.nanoTime();
            logger.info(String.format("%s took %d ns", joinpoint.getSignature(), (end - start)));
            return result;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
        
}

This advice is expected to capture the time taken by the methods in DefaultInventoryService. So now to wire this advice to the DefaultInventoryService spring bean:
 <bean id="auditAspect" class="org.bk.inventory.aspect.AuditAdvice" />

 <aop:config>
  <aop:aspect ref="auditAspect">
   <aop:pointcut id="serviceMethods" expression="execution(* org.bk.inventory.service.*.*(..))" />

   <aop:before pointcut-ref="serviceMethods" method="beforeMethod" />  
   <aop:around pointcut-ref="serviceMethods" method="aroundMethod" />
   <aop:after-returning pointcut-ref="serviceMethods" method="afterMethod" /> 
  </aop:aspect>
 </aop:config>

This works by first defining the "pointcut" - the places(in this example, the service methods) to add the cross cutting concern(capturing the method execution time in this example) to. Here I have defined it using a pointcut expression -
execution(* org.bk.inventory.service.*.*(..))
, which is essentially selecting all methods of all the types in the org.bk.inventory.service package. Once the pointcut is defined, it defines what needs to be done around the pointcut(the advice), using the expression:
<aop:around pointcut-ref="serviceMethods" method="aroundMethod" />
This basically says, that around every method of any service type, execute the aroundMethod of AspectAdvice that was defined earlier. Now, if the service methods are executed, I would see the advice getting invoked during the method execution, the following is a sample output if DefaultInventoryService, createInventory method is called:
org.bk.inventory.service.InventoryService - Create Inventory called
org.bk.inventory.aspect.AuditAdvice - Inventory org.bk.inventory.service.InventoryService.create(Inventory) took 82492 ns
Spring's AOP implementation works by generating a dynamic proxy at runtime for all the target beans, based on the defined pointcut.
Links to all sessions on AOP:
AOP Session 1 - Decorator Pattern using Java Dynamic Proxies
AOP Session 2 - Using Spring AOP - xml based configuration
AOP Session 3 - Using Spring AOP - @AspectJ based configuration - with/without compile time weaving
AOP Session 4 - Native AspectJ with compile time weaving
AOP Session 5 - Comprehensive Example

No comments:

Post a Comment