Tuesday, August 9, 2011

A simple introduction to AOP - Session 1

Why use AOP, a simple way to answer this question is to show an implementation of a cross cutting concern without using AOP.

Consider a simple service and it's implementation:
public interface InventoryService {
    public Inventory create(Inventory inventory);
    public List list();
    public Inventory findByVin(String vin);
    public Inventory update(Inventory inventory);
    public boolean delete(Long id);
    public Inventory compositeUpdateService(String vin, String newMake);
}

and its default implementation:
public class DefaultInventoryService implements InventoryService{
    
    @Override
    public Inventory create(Inventory inventory) {
        logger.info("Create Inventory called");
        inventory.setId(1L);
        return inventory; 
    }

    @Override
    public List list() {
        return new ArrayList();
    }

    @Override
    public Inventory update(Inventory inventory) {
        return inventory;
    }

    @Override
    public boolean delete(Long id) {
        logger.info("Delete Inventory called");
        return true;
    }
....

This is just one service. Assume that there are many more services in this project.

So now, if there were a requirement to record the time taken by each of the service methods, the option without AOP would be something along the following lines. Create a decorator for the service:
public class InventoryServiceDecorator implements InventoryService{
    private static Logger logger = LoggerFactory.getLogger(InventoryServiceDecorator.class);
    private InventoryService decorated;

    @Override
    public Inventory create(Inventory inventory) {
        logger.info("before method: create");
        long start = System.nanoTime();
        Inventory inventoryCreated = decorated.create(inventory);
        long end = System.nanoTime();
        logger.info(String.format("%s took %d ns", "create", (end-start)) );
        return inventoryCreated;
    }
This decorator would essentially intercept the call on behalf of the decorated, record the time taken for the method call while delegating the call to the decorated object.

Imagine doing this for all the methods and all the services in the project. This is the scenario that AOP addresses, it provides a way for the cross cutting concerns(Recording time for the service method calls for eg.) to be modularized - to be packaged up separately without polluting the core of the classes.

To end the session, a different way to implement the decorator would be using the dynamic proxy feature of Java:


public class AuditProxy implements java.lang.reflect.InvocationHandler {
    
    private static Logger logger = LoggerFactory.getLogger(AuditProxy.class);
    private Object obj;

    public static Object newInstance(Object obj) {
        return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), new AuditProxy(obj));
    }

    private AuditProxy(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        Object result;
        try {
            logger.info("before method " + m.getName());
            long start = System.nanoTime();
            result = m.invoke(obj, args);
            long end = System.nanoTime();
            logger.info(String.format("%s took %d ns", m.getName(), (end-start)) );
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
        } finally {
            logger.info("after method " + m.getName());
        }
        return result;
    }
}

So now, when creating an instance of InventoryService, I would create it through the AuditProxy dynamic proxy:
InventoryService inventoryService = (InventoryService)AuditProxy.newInstance(new DefaultInventoryService());

the overridden invoke method of java.lang.reflect.InvocationHandler would intercept all calls to the InventoryService created in this manner, where the cross cutting concern of auditing the method call time is recorded. This way the cross cutting concern is modularized to one place(AuditProxy), but still needs to be explicitly known by the clients of InventoryService, when instantiating InventoryService.

In the next few sessions, I will demonstrate how this can be more cleanly accomplished using Spring AOP, Spring AOP with @AspectJ, AspectJ, @AspectJ with compile time weaving and finish it off with a comprehensive example.

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

6 comments:

  1. Good example Biju, you made look things simpler. AOP is an important concept and worth including in my list of Spring questions. Thanks.

    ReplyDelete
  2. is there any plan of introducing AOP feature in JDK in future versions?

    ReplyDelete
  3. Hi,
    I am expressing my views after bookmarking it, I doesn't want to miss such a good article which have complete info about AOP. You have kept to so well, that it felt easy for me...
    Thanks a lot.

    ReplyDelete
  4. Nice explanation with good example . its fine now , once i was not able to understand it. thanks

    ReplyDelete
  5. Got neat and explanation of AOP along with source code. Will add it to my blog. Thanks

    ReplyDelete
  6. Good post!! The point you have mentioned in interesting, good way to learn more while testing and this experience will be useful further when working on a huge projects…

    ReplyDelete