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.
- Create an abstract way to apply you own aop on a targeted object.
- Create your own AOP say BeforeHandler and AfterHandler where former does some work before method execution and later does after method execution.
- 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.
- Provide your own business logic or cross cutting concern.
- 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);
Configuration
All the above code snippets are very brief to provide you more clarity about the structural implementation. It is always better to have an actual taste/test to bring the reality. Download the complete project from the following github link and configure it in your favorite java editor and run the test class to see the effect.
Conclusion
Hope you enjoy my small article on AOP in Java, post some comment to enrich and enhance the knowledge on both the sides. In this article I have created for before and after, I leave “Around” and “Throw” AOP for the reader.
If you have an interface and implementation, you can simply apply the decorator pattern to the same effect, without the need for proxies and reflection.
You are correct Tom, we can also achieve using Decorator Pattern, I have written a template being inspired by Spring AOP template formatting.
Simply great Deba