Friday, May 28, 2010

Get a Spring Aspect Working

After some time away from AOP, I've found myself forgetting the bare essentials of getting off the ground - i.e., what's the least I have to do just to prove that my aspect is getting invoked?

Assuming a Spring 3.0 environment, here is the summary:
  1. Create the aspect
  2. Create a Spring context file that declares AOP
  3. Use Spring to instantiate both the aspect and the application object
  4. Run your test case to prove it's working
Here's a bare-bones aspect:

package com.mybiz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
@Component
public class MyAspect
{
    // Flag indicating that a given pointcut was in fact invoked.
    public boolean madeIntercept = false;

    @Pointcut("execution(* com.mybiz.MyApp.myMethod(..))")
    public void myMethodPointcut() {}

    @After("myMethodPointcut()")
    public void doSomething() { madeIntercept = true; }
}

Here's a simple Spring file, named spring-test-aspect.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.mybiz"/>

    <aop:aspectj-autoproxy/>

</beans>

The aspect will automatically get Spring-managed since it uses the @Component annotation, and component scanning declared above includes its package. Use the same approach with your application object:

package com.mybiz;
@Component
public class MyApp
{
    public void myMethod() {}
}

Filling in the method is irrelevant; all we're trying to do is prove that if that method is invoked, that the aspect will  intercept the control flow afterwards. Here's a test case that does this for us:

package com.mybiz;

@ContextConfiguration(locations ={"file:spring-test-aspect.xml"})
public class MyAspectTest extends AbstractTestNGSpringContextTests
{
    @Resource
    private MyAspect myAspect;

    @Resource
    private MyApp myApp;

    @PostConstruct
    public void init() { assert myAspect != null; assert myApp != null; }

    @Test
    public void confirmInterception() 
    { 
        assert !myAspect.intercepted();
        myApp.myMethod(); 
        assert myAspect.intercepted();
    }
}


There's the proof-of-concept; from here, you'd add real functionality to your application class and decorate that functionality with the aspect. I should offer a fair warning: the above snippets have not been actually compiled and run, rather simply copy-pasted with various names changed to be more general purpose (as such, it might not be totally correct). So, if you bump into any problems - you have my apologies, but I'm sure the problems will be trivial.