Aspects for good, decoupled design

Basics

We’ve all learnt that good, object oriented design is based on some kind of layering of the concepts; one layer has a very well defined scope, like for example database access, presentation and the alike. A layer is not allowed to use any other layer, but the one and only the one  right below it. A typical such architecture would look something like the one on the following diagram (drawn with creately.com):

layer
Typical layered architecture

Every class to be written, is perfectly contained in one and only one layer. For example, the class UserDataConverter would probably be contained in layer “Data Conversion”; it has nothing to do with data access, for example, nor with displaying information.

The situation, however, gets completely different when dealing with requirements that are all over the system – like transactions or logging, for example. These things are spread in an application. They are not tied to a specific layer, but rather to all of the layers. If you want transaction management, that will possibly impact all the layers from the top to the bottom; you can’t do it in the DAO level alone. A transaction may start with a click on the GUI, and propagate all the way down to the database – should anything go wrong at any level, the transaction rolls back.

Let’s take an example: we want to perform some profiling, to measure the running time for some of our methods. These methods are not specific to any layer, they can be anywhere in our system, from the data access layer up to presentation. If we’d represent this feature on the diagram above, it could look something like this:

asp
Profiling feature breaks the boundaries of layers

We cannot say that profiling should apply to business logic alone, as we may as well want to know how many time does it take to convert data between different formats. Now, a very bad solution to this problem would be getting the current time in the beginning and the end of every method we want to be profiled, and doing something with the result. This solution would create some awful spaghetti code with lot of duplication, not to mention the tedious work one should be doing for a large, legacy system with hundreds of methods to be profiled.

A nicer option would be creating some kind of utility class, to which you could tell that the method has started executing, and has finished eventually. The utility would take care of computing the elapsed time and handling the resulting numbers. However, this would still be pretty tightly coupled, with two invocations per method.

public void longRunningMethod() {
  profiler.methodStarted();
  // ...
  profiler.methodFinished();
}

It may seem that we’ve now got rid most of the code multiplication, but those start/stop methods are still spread all around our code base. How do we clean up our code, then?

Aspects to the rescue

Thank God, there is a way to handle this situation (and of course not only this one; it’s also suitable for error handling, self-* operations, customized access management, and almost any feature that spans through layers): using aspects (as we will see, aspects are also called interceptors in some frameworks).

In order to understand how aspects actually work, let’s begin with examining method calls. A usual method call looks like this (obvious diagram to follow):

call
Ordinary method call

With the usage of aspects, this is slightly changed. The object on which the method is called, will be coated in a proxy object to which all the method calls will be routed. Whenever a method call is made to the proxy object, the call will be ‘hijacked’, and the proxy will be given a chance to perform different operations before, after or around (before+after) the method call. For this to work, you will need some kind of a container (either Spring or JavaEE can be a good choice) or third party library, as you cannot do this with Java right out of the box.

As you can see on the diagram above, the container directs the call of otherMethod to the proxy around object2, and performs some operations before and/or after the original method call. This way object2.otherMethod can always stay the same, the only things that change are the before and after methods, but as you are going to see, they are present at only one place within the whole system. The class that implements the logic for before and after is called the Aspect.

With such an aspect, the code for measuring elapsed times for methods can be totally decoupled from the rest of the application. We can come up with an aspect to hold logic that will be run around (remember, around = before+after) the method call:

public class LatencyTracerAspect {
    //...
    public void interceptAround() {
      //code for getting start time
      // run original, business method
      //code for measuring elapsed time
    }
}

All that remains, is to make the container notice our aspect, and tell it what methods to intercept. This is, nevertheless different for each and every container – see below for examples.

Simple, elegant, 100% reusable and totally independent of the business logic (single responsibility principle for the win).

 

Ups and downs of aspects

As mentioned earlier, aspects are very useful when some application requirements would break the boundaries of different layers. In these cases it’s best to organize these features such a way they are easily accessible everywhere, without code duplication – through aspects. However, it’s a very bad practice to extract pieces of business logic into aspects. A poorly implemented aspect with surprising side effects can be the best place for bugs to hide. It is perfectly OK to implement an aspect to check whether a certain XML file is well formed or not, before starting to work with it, but do not send back a reject message to the client from the aspect if the validation fails. Throw an unchecked exception instead, and let business logic handle functional requirements like sending reply messages.

Usually, using before or around aspects you can manipulate the parameters passed in to a method call; with after and around aspects you can modify the return values of the those methods intercepted. Even if it’s technically possible, (most of the times) it is not a good practice to do so. Again, figuring out that the system is throwing exceptions just because some aspect keeps changing the input parameters or the return values is, well, at least tedious and frustrating.

Don’t overuse aspects. Although many times they come in handy, they can make debugging a nightmare. Not to mention that normally aspects will not run with the unit tests suite (as there is no container present to interpret them), so watch out for logic hiding in them. Test them as well as your regular business objects.

 

Examples

As we know the theory by now, let’s apply this knowledge in practice. The code examples to follow are not meant to make you an aspects/interceptors expert, just to exemplify how the things mentioned above can be achieved with different containers. In the following, we are going to implement the profiling aspect using three different containers: Spring, EJB and CDI.

Spring

In Spring, each method to be intercepted should be contained in a Spring bean (note that only managed beans will be intercepted, not those created with the new keyword), and the same is true for the caller method. You cannot intercept a method call on a non-Spring-bean.

We are going to create an annotation, through which we will tell the container that we want our method intercepted. For this, we create a usual, nothing-fancy annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Profiled {

}

That’s it, the signaler annotation is done. Now, we shall create an aspect, and bind our annotation to it.No rocket science here, all we have to do is type in the following code:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class ProfilingAspect {

	@Around("@annotation(Profiled)")
	public Object intercept(ProceedingJoinPoint context) {
		Object retval = null;

		long start = System.nanoTime();

		try {
			retval = context.proceed();
		} catch (Throwable t) {
			// Some clever exception handling
		}

		long end = System.nanoTime();
		System.out.println("------------------------------");
		System.out.println("Profiling result: " + (end - start) + " nanos.");
		System.out.println("------------------------------");
		return retval;
	}
}

Notice the @Aspect annotation at the top of of the class. Don’t fall in a common trap here: in your config file, you still have to define this aspect as a bean; thus the @Aspect annotation alone is not enough. Also, in the config file (I am taking a stand on Java class based configuration) you have to enable AspectJ style proxying, using the  @EnableAspectJAutoproxy annotation. Finally, don’t forget to call proceed() on the context object, so the program flow can advance.

Also note the @Around(“@annotation(Profiled)”) annotation. The expression (aka Pointcut) tells Spring to intercept all methods annotated with @Profiled. So, let’s create a method that can be intercepted (probably the easiest part):

public class Intercepted {
	@Profiled
	public void sayIt(String name) {
		System.out.println("We're happy, " + name);
	}
}

Pretty simple, huh? From now on, every method annotated with @Profiled will be intercepted by the container, provided that these annotations are placed on managed beans’ public methods.

Note:

  • The container will not intercept private method calls.
  • Don’t forget to put aspectjveawer.jar on your classpath.
  • You can play around with the context object; you can even change what’s getting passed and returned from a method.
  • Method calls from within the same bean are not intercepted
  • You can read further on this topic here

JavaEE

EJBs

(Aspects are called Interceptors in the enterprise world) In this case, the implementation for the profiler is somewhat easier. As this is JavaEE, you don’t have to place different kinds of jars on the classpath, as your container will have everything needed – right out of the box.

We don’t have to create annotations here, as the binding is solved differently with EJBs. Our interceptor class is somewhat similar to the one before, and looks like this:

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class Interceptor {

	@AroundInvoke
	public Object intercept(InvocationContext context) {
		//Logic is the same as before...
	}
}

The binding in the EJB code is done using the @Interceptors annotation applied on EJB class level(in which case all public methods will be intercepted) or method level(only those annotated methods will be intercepted). Our intercepted bean/method will be nothing more than:

import javax.ejb.Stateless;
import javax.interceptor.Interceptors;

@Stateless
// @Interceptors(Interceptor.class) could have been declared here too
public class Intercepted {

    @Interceptors(Interceptor.class)
	public void sayIt(String name) {
		System.out.println("We're happy," + name);
	}
}

Note:

  • Internal method calls (within the same bean) are not intercepted, no matter how the methods are annotated.
  • Private methods (resulting from the bullet above) are not intercepted
  • You are free to have several interceptors for the same bean/method. Those interceptors have to be enumerated like this: @Interceptors({Interceptor1.class, Interceptor2.class})

CDI interceptors

In the case of CDI interceptors we need an intercepted bean, an interceptor, and an interceptor binding – which is just like in case of our Spring example, a custom annotation.

With the help of Interceptor Bindings, we can tell the container which methods should be intercepted. The interceptor binding is not much different from our first @Profiled annotation; we only need to add @InterceptorBinding to it:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Profiled {
}

An interceptor will be annotated with @Interceptor as well as the annotation dealing with the interceptor binding – it is somewhat similar to a Spring pointcut.  This will bind our @Profiled annotation to the interceptor logic:

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@Profiled
public class CdiInterceptor {

	@AroundInvoke
	public Object around(InvocationContext context) throws Exception{
        //... Logic same as before ...
    }

The intercepted class is, again, the same as with Spring:

	@Profiled
	public void sayIt() {
		System.out.println("CDI bean for the win");
	}

Finally, we have to enable CDI and the interceptors too. By default, CDI is turned off, you can switch it on by placing an empty beans.xml file in your META-INF directory. This is not enough in our case, though. CDI interceptors are also disabled by default; in order to enable them, we will make our new interceptor class visible to CDI container using the same beans.xml:

<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
                         http://java.sun.com/xml/ns/javaee
                         http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

	<interceptors>
		<class>blog.intercept.CdiInterceptor</class>
	</interceptors>
</beans>

To read more in depth about CDI interceptors, go to: http://docs.jboss.org/weld/reference/1.0.0/en-US/html/interceptors.html.

Note:

  • Internal method calls (within the same bean) are not intercepted, no matter how the methods are annotated.
  • Private methods (resulting from the bullet above) are not intercepted

 

Conclusions

Aspects can be a very efficient and clean means of decoupling business logic from omnipresent features. This way the business methods can deal with business requirements while aspects add the decorator functionality for handling common, non-layer specific tasks.

Aspects are easy to get started with, as chances are high that larger systems are already based on either Spring or JavaEE. They both handle aspects (interceptors) very well, without writing complex code to enable them.

Aspects over-usage can have a bad impact on the system, as it can become hard to track what feature is handled by what aspect. Also, take care not to implement features carrying business value as aspects.

Advertisements

Author: tamasgyorfi

Senior software engineer, certified enterprise architect and certified Scrum master. Feel free to connect on Twitter: @tamasgyorfi

One thought on “Aspects for good, decoupled design”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s