May 13, 2004
Integrating AspectJ and AOP Alliance based aspects
I was taking part in the AOP panel discussion at last weeks TSS symposium, and listening to Rod Johnson and Bob Lee describe how dynaop and Spring can share aspects since they both implement the AOP Alliance interfaces. Sounds like both projects have some great aspects already written, and with more to come the obvious question crossed my mind: can I use aspects written to the AOP Alliance interfaces from within AspectJ?
A few hours later and I had a solution that lets you use the expressive power of AspectJ's pointcut language to invoke advice (interceptors) written to the AOP Alliance interfaces, and to freely mix and match AspectJ and AOP Alliance advice in the same program, even applying at the same join points. Here's how it looks...
First a little background, the AOP Alliance have basically defined a set of interfaces that define method interception and constructor interception implemented advice, and a join point interface hierarchy that provides access to the (java.lang.)Method or Constructor being advised, the currently executing object ('this'), and the call parameters. Unlike in AspectJ, the Joinpoint interface in the aopalliance.jar also contains the proceed()
method that is to be called by an interceptor if and when it decides to allow computation at the join point to proceed.
Here's how a simple method interceptor looks written to the AOP Alliance interfaces:
public class MyMethodInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation jp) throws Throwable { System.out.println("About to invoke: " + jp.getMethod().getName()); Object ret = jp.proceed(); System.out.println("Completed invocation of: " + jp.getMethod().getName()); return ret; } }
I've written an AspectJ library aspect AOPAllianceAdapter
that lets you invoke such interceptors as regular AspectJ advice. First I'll show you how it looks to use the aspect, and then we can dive into the details.
public aspect MyAOPAllianceAdapter extends AOPAllianceAdapter { private MethodInterceptor myMethodInterceptor = null; /** * provide definition of getMethodInterceptor from super-aspect */ protected MethodInterceptor getMethodInterceptor() { if (myMethodInterceptor == null) myMethodInterceptor = new MyMethodInterceptor(); return myMethodInterceptor; } /** * provide definition of targetJoinPoint pointcut from super-aspect */ pointcut targetJoinPoint() : within(org.xyz..*); }
The aspect you see above will invoke the method interceptor at each execution join point matched by the targetJoinPoint
pointcut definition. That's all there is to it. You can also override the getConstructorInterceptor method if you want to use an AOP Alliance constructor interceptor instead or in addition.
For the curious, here's the definition of the library aspect - the only tricky part is building the closure so that when an AOP Alliance interceptor calls proceed()
on a join point AspectJ will do the right thing.
public abstract aspect AOPAllianceAdapter { /** * sub-aspects override this method to tell the adapter which interceptor to drive. */ protected abstract MethodInterceptor getMethodInterceptor(); /* * constructor interceptors are less often used, so we provide a default implementation */ protected ConstructorInterceptor getConstructorInterceptor() { return null; } /** * sub-aspects define this pointcut to determine the join points at which the interceptor(s) will be * invoked. */ protected abstract pointcut targetJoinPoint; // method interceptors should only be triggered at method-execution join points, and // constructor interceptors at constructor-execution join points pointcut methodExecution() : execution(* *(..)); pointcut constructorExecution() : execution(new(..)); /** * invoke method interceptor at target execution join points */ Object around() : targetJoinPoint() && methodExecution() { MethodInvocationClosure mic = new MethodInvocationClosure(thisJoinPoint) { public Object execute() { return proceed(); } } MethodInterceptor mInt = getMethodInterceptor(); if (mInt != null) { try { return mInt.invoke(mic); } catch(Throwable t) { throw new SoftException(t); } } else { return proceed(); } } /** * invoke constructor interceptor at target execution join points */ Object around() : targetJoinPoint() && constructorExecution() { ... similar to above definition } }
The helper class MethodInvocationClosure implements the AOP Alliance MethodInvocation interface. It defines the proceed() method from the interface to call execute (since proceed() has a special meaning inside AspectJ's around advice). An anonymous subclass of the closure is created inside the around advice, that proceeds (AspectJ proceed) when the execute method is invoked (i.e., when the AOP Alliance adapter wants to proceed at the join point).
I'll post the code and test cases for this somewhere publicly available - probably in the AspectJ samples 'sandbox' in CVS - so that you can try this out for yourself.
Posted by adrian at May 13, 2004 04:27 PM [permalink]
Comments
Cool!
Posted by: Visitor at June 4, 2004 05:17 AM
Post a comment
Thanks for signing in, . Now you can comment. (sign out)
(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)