AspectWerkz – Declaring war on spaghetti code
I got acquainted with Aspect Oriented Programming (AOP) concepts during my college days. It was my seminar topic and one of the most ‘talked about’ programming paradigm in those days. Today the concept has grown to a great extend and there are few very good tools that support AOP.
Aspect Oriented Programming
Aspect Oriented Programming (AOP) is a programming paradigm, also referred to as a compliment to Object Oriented Programming (OOP). AOP tries to increase the modularity by helping in separating concerns. Let’s start with a sample code with cross-cutting concern.
import org.apache.log4j.Logger; public class MyClass { Logger logger = Logger.getLogger( MyClass.class.getName() ); public void myMethod(int i) { logger.debug( "Entering myMethod, i=" + i ); // Actual business logic goes here logger.debug( "Exiting method myMethod" ); } }
I am sure that majority of the programmers must have seen similar code; unrelated and tangled with each other. In the above code snippet, the ‘logger’s responsibility is to trace the entry and exit of a method. This get in the way of our actual business logic. It sometimes becomes difficult to manage and understand code. This is called cross-cutting concerns, where entirely different concerns or aspects cut in between each other. This type of code is also known as spaghetti code.
Another similar example is the use of transaction managers. You may find utx.begin() , utx.commit() , utx.rollback() , and lot more code related to transaction management tangled with your business logic. Now imagine transaction management, exception handling, logging, and all sorts of mayhem in a single method. “Ugly”, isn’t it. That’s where AOP comes to rescue.
Aspect Oriented Concepts
I will be demonstrating each of these concepts through examples later on. For now, we can briefly understand various concepts involved in AOP.
- Aspects: Aspects are entities similar to ‘Classes’ in OOP. We can create Aspects to handle our secondary concerns and leave the business logic untouched. An aspect can be broken down into two parts: Pointcuts and Advice.
- JoinPoints: These are basically places in the actual business logic where you wish to insert some miscellaneous functionality that is necessary but not being part of the actual business logic. Some examples of JoinPints are: method call, method returning normally, method throwing an exception, instantiating an object, referring an object, etc…
- Pointcuts: Pointcuts are something like regular expressions which are used to identify joinpoints. Pontcuts are expressed using “pointcut expression language”. Pointcuts are points of execution flow where the cross-cutting concern needs to be applied. There is a difference between Joinpoint and Pointcut; Joinpoints are more general and represents any control flow where we ‘may choose to’ introduce a cross-cutting concern while pointcuts identifies such joinpoints where ‘we want to’ introduce a cross-cutting concern.
- Advice: Like I mentioned in Aspects, Advice is the other part where we write what should be done when a pointcut is mathced. There are different kinds of advice: before, after and around. I am not going into these details right now.
- Aspect Weaving: Aspect weaving is the process where the advice in the aspects are weaved into the business logic. Different types of weaving are: compile time, load time and runtime. AspectJ and AspectWerkz primarly uses compile time weaving while SpringAOP uses runtime weaving.
Introducing AspectWerkz
AspectJ is the most advanced of all AOP frameworks available in the market. Almost all Java-based AOP frameworks follow the standards set by AspectJ. Aspectwrekz does not provide the same level of features as AspectJ does, but it still will fit in almost all situations.
If you visit AspectWerkz website you might see the introduction of Aspectwerkz as “AspectWerkz is a dynamic, lightweight and high-performant AOP framework for Java”. Aspectwrekz is probably the most easy to understand and implement. AspectWerkz support a fine-grained pattern language for matching join points.
The most basic pieces of and AOP based application are:
- Business Class – Class that contains the gist of the application.
- Aspect – Additional functionality that is needed / required but not part of the actual business.
- Aspect Definition File – Tells the weaver which aspects to weave and where. By default aspectwerkz uses META-INF/aop.xml from the classpath.
My First Aspect Example
Now lets start with a small ‘Hello World’ example to get to know how to use aspectwerkz in a project. Please refer aspectwrekz website for tutorials on how to use the framework.
First the Business class (HelloWorld.java).
package test; public class HelloWorld { public void greet() { System.out.println("Greetings!"); } public static void main(String args[]) { HelloWorld hw = new HelloWorld(); hw.greet(); } }
Now our Aspect class (MyFirstAspect.java).
package test; import org.codehaus.aspectwerkz.joinpoint.JoinPoint; public class MyFirstAspect { public void beforeGreeting() { System.out.println("before greeting..."); } public void afterGreeting() { System.out.println("after greeting..."); } }
Finally, the Aspect Definition File (META-INF/aop.xml).
<aspectwerkz> <system id="AspectWerkzExample"> <package name="test"> <aspect class="MyFirstAspect"> <pointcut name="HelloWorldMethods" expression="execution(* test.HelloWorld.*(..))"/> <advice name="beforeGreeting" type="before" bind-to="HelloWorldMethods"/> <advice name="afterGreeting" type="after" bind-to="HelloWorldMethods"/> </aspect> </package> </system> </aspectwerkz>
The output when you compile, weave and run the above example would be:
before greeting... Greetings! after greeting...
Wow!!! We have written our first aspect…
Logger implemented as an Aspect
Now lets see how we can use AOP to improve our Logger example. First remove all the logging stuff from our Business class (MyClass.java).
package test; public class MyClass { public int multiply(int i, int j) { // Only business logic goes here.. return i*j; } public static void main(String args[]) { MyClass obj = new MyClass(); obj.multiply(2, 3); } }
Now our Aspect class (MethodCallTracer.java).
package test; import org.codehaus.aspectwerkz.joinpoint.JoinPoint; import org.codehaus.aspectwerkz.joinpoint.MethodRtti; import org.apache.log4j.Logger; public class MethodCallTracer { Logger logger; public void beforeMethodCall(JoinPoint joinPoint) { logger = Logger.getLogger( joinPoint.getCalleeClass().getName() ); logger.debug("Entering method: " + joinPoint.getCalleeClass().getName() + "." + joinPoint.getSignature().getName()); Object[] parameters = ( (MethodRtti)joinPoint.getRtti() ).getParameterValues(); logParameters( parameters ); } public void afterMethodCall(JoinPoint joinPoint) { logger = Logger.getLogger( joinPoint.getCalleeClass().getName() ); logger.debug("Exiting method: " + joinPoint.getCalleeClass().getName() + "." + joinPoint.getSignature().getName()); MethodRtti rtti = (MethodRtti)joinPoint.getRtti(); if( !void.class.equals( rtti.getReturnType() ) ) logger.debug( "Returning: " + rtti.getReturnValue() ); } private void logParameters(Object[] parameters) { if(parameters.length > 0) logger.debug("Parameters: " + getCSV( parameters ) ); } private String getCSV(Object values[]) { if(values == null) { return null; } StringBuilder buf = new StringBuilder(); for (int i = 0; i < values.length; i++) { buf.append( values[i] ); if (i < (values.length - 1)) { buf.append( "," ); } } return buf.toString(); } }
Finally, the Aspect Definition File (META-INF/aop.xml).
<aspectwerkz> <system id="AspectWerkzExample"> <package name="test"> <aspect class="MethodCallTracer"> <pointcut name="anyMethod" expression="execution(* test.*.*(..))"/> <advice name="beforeMethodCall" type="before" bind-to="anyMethod"/> <advice name="afterMethodCall" type="after" bind-to="anyMethod"/> </aspect> </package> </system> </aspectwerkz>
The output would be something like:
Entering method: test.MyClass.myMethod Parameters: 2, 3 Exiting method:test.MyClass.myMethod Returning: 6
I hope you had fun with this sample. More to come!!!