adam bien's blog

Lightweight AOP with plain Java SE 1.3+, without additional libraries, frameworks and tools 📎

Aspect Oriented Programming is often used for trivial things like logging, static security checks or performance measurements.
Although AOP separates the infrastructure from the business and so makes the code more maintainable, it introduces also some additional complexity. AOP relies on (static/dynamic) bytecode manipulation often with own classloaders or java agents. This can be funny in managed environments like RCP-Frameworks or Java EE containers :-).
Also some additional libraries have also be shipped, which introduces some risk in the deployment.

Interestingly enough: Java has already a built-in a simple interception mechanism called: dynamic proxy. It is actually very old (since JDK 1.3+), so it seems to be almost forgotten.
The key element is one simple interface from the package java.lang.reflect, called InvocationHandler. It comes with a simplistic method:  

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

This Handler-interface has to be realized by a custom class which in fact already represents the cross cutting aspect. In our case it is a simple logger:

public class TracingInvocationHandler implements InvocationHandler{

    public Object invoke(Object proxy, Method method, Object parameters[])
        throws Throwable {
            Object retVal;
            Method targetMethod = null;
                try {
                long start = System.currentTimeMillis();
                System.out.println("Invoking: " + formatMethodOutput(method,parameters));
                targetMethod = getMethodForTarget(method,method.getParameterTypes());
                retVal = targetMethod.invoke(target, parameters);
                //performance mesurement
                } catch (InvocationTargetException e) {
                 //Exception reporting 
                        e.getTargetException().toString());
                 //rethrowing the exception
                    throw e.getTargetException();
                } catch (Exception e) {
                    throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
                }   
        return retVal;
    }


Now we have to somehow inject this additional behavior to the existing Java-code. This can be done with the static method java.lang.reflect.Proxy.newProxyInstance.
And now some limitation of this approach are visible:
  1. The Proxy-class is only able to decorate interfaces
  2. It is hard to inject the additional behavior to existing spaghetti code...
However, existing factories are perfect place for the product decoration. The decoration can be also factored out into a utility. Then we are even able to intercept JDK-classes (the class Decorator only wraps the Proxy.newProxyInstance methods and simplyfies the user interface):

        Map map =(Map) Decorator.trace(new HashMap(),Map.class);
    System.out.println("Map: "  +map.getClass().getName());
    map.put("hallo","world");
    map.get("hallo");
    map.size();

The execution of this piece of code creates the following output:

Map: $Proxy0
Invoking: java.lang.Object java.util.Map.put(java.lang.String hallo,java.lang.String world)
put returns: null performed in: 0 ms!
Invoking: java.lang.Object java.util.Map.get(java.lang.String hallo)
get returns: world performed in: 0 ms!
Invoking: int java.util.Map.size()
size returns: 1 performed in: 0 ms!

You will find the whole, fully functional, project in p4j5 (Check-Out the DynamicProxy NB-project and "run" it).