Tuesday, 17 March 2015

Create your own AOP in Java

Introduction


As you know AOP is one of the best features provided by Spring framework which provides utmost flexibility while achieving cross cutting concerns. Have you thought of how AOP works in Spring ? Sometimes this is the question asked in case of senior level technical interview. Sometimes this question becomes more significant when it comes to only core java. Recently one of my friend went to attend the interview and he faced an embarrassing question about how to use AOP only in core java without using Spring and related libraries. In this article I will provide you an outline about how to create your own AOP only in core java of course with certain limitations. This is not a comparative study between Spring AOP and Java AOP. However you can achieve AOP in java to certain extent using proper design patterns.

All of you know AOP and how to use it using Spring framework , this article will give you an insight into a way to achieve AOP in java without using Spring. For your information Spring uses both JDK proxy as well as CGlib to provide AOP implementation. JDK dynamic proxy provides a flexible approach to hook a method and to perform certain operation with certain limitations. The limitation is that there should be an interface and there should be an implementation for that interface. Nothing is clear as of now. Let us take an example. We have a Calculator through which we want to perform some mathematical operations. Let us consider for division of a number by another number. Now question is somebody has already provided an implementation for the division operation in the core framework, is it possible to highjack in the method to perform additional validation/s ? Yes it is. To achieve this I provide below the code snippet for this simple case. The basic abstract code is given below.

public interface Calculator {
    public int calculate( int a , int b);
}

The code for the implementation is given below.

public class CalculatorImpl implements Calculator {

    @Override
    public int calculate(int a, int b) {
        return a/b;
    }
}

Imagine that above codes have been freezed and there cannot be more modification in the core, but you have to achieve the functionality without any issue. How to do it ? Let’s use the feature of JDK dynamic proxy.

public class SomeHandler implements InvocationHandler {

// Code omitted for simplicity…..

    @Override
    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
// Your complex business validation and logic
        Object result = method.invoke(targetObject ,params);
        return result;
    }
}

Let us see the test class to perform our desired functionality using our jdk dynamic proxy.

public static void main(String[] args) {
        CalculatorImpl calcImpl = new CalculatorImpl();
        Calculator proxied = (Calculator) getProxy (Calculator.class, calcImpl,
                new SomeHandler(calcImpl));
        int result = proxied.calculate(20, 10);
        System.out.println("FInal Result :::" + result);
    }

As you have seen we have just provided a hooking implementation by simply implementing the great interface call “InvocationHandler”. As per java documentation, it processes a method invocation on a proxy instance.

Now you have seen in the above that we can do something using InvocationHandler’s invoke() method to get the desired result. Now question comes to our mind that how can we do something before and after the actual method execution. Is it possible that can we do something before a method get executed and do something after the method gets executed ? To make it more specific, can we add multiple aops(before, after, around) to hook a method ? We can achieve it by making a simplified code template. Let us follow the steps below to achieve it.

  1. Create an abstract way to apply you own aop on a targeted object.
  2. Create your own AOP say BeforeHandler and AfterHandler where former does some work before method execution and later does after method execution.
  3. Create a proxy class to make developers friend so that all the aop handlers and targetd object can be passed easily to create a hook.
  4. Provide your own business logic or cross cutting concern.
  5. Finally create the proxy object by passing the required parameters.

Brief technical implementation


Create the abstract handler in the following manner.

public abstract class AbstractHandler implements InvocationHandler {

    private Object targetObject;

    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }
}

Create the flexible developers friendly handlers like BeforeHandler and AfterHandler.

apublic abstract class BeforeHandler extends AbstractHandler {

    public abstract void handleBefore(Object proxy, Method method, Object[] args);

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        handleBefore(proxy, method, args);

        return method.invoke(getTargetObject(), args);

    }
}
public abstract class AfterHandler extends AbstractHandler {

    public abstract void handleAfter(Object proxy, Method method, Object[] args);

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(getTargetObject(), args);
        handleAfter(proxy, method, args);
        return result;
    }
}

Create the proxy utility.

public class ProxyFactory {

    public static Object getProxy(Object targetObject,
            List<AbstractHandler> handlers) {
            //Code to get the proxy
            return proxyObject;
        } else {
            return targetObject;
        }
    }
}

Now the test harness code snippet is given below.

CalculatorImpl calcImpl = new CalculatorImpl();
BeforeHandler before = new BeforeHandlerImpl();
AfterHandler after = new AfterHandlerImpl();
List<AbstractHandler> handlers = new ArrayList<AbstractHandler>();
handlers.add(before);
handlers.add(after);
Calculator proxy = (Calculator) ProxyFactory.getProxy(calcImpl,
                handlers);
int result = proxy.calculate(20, 10);