Cool Groovy aspect in Spring

I’ve been teaching a lot of Spring framework classes lately. In one of them, we have a unit on Aspect Oriented Program (AOP) in Spring. Spring provides all the necessary infrastructure to make AOP doable. You can define aspects using annotations, and Spring will auto-generate the necessary proxies to implement them.

As an example, the materials (written for Java, of course), presented an aspect similar to this:

package mjg.aspects;

import java.util.logging.Logger;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class PropertyChangeTracker {
    private Logger log = Logger.getLogger(PropertyChangeTracker.class.getName());
    
    @Before("execution(void set*(*))")
    public void trackChange(JoinPoint jp) {
        String method = jp.getSignature().getName();
        Object newValue = jp.getArgs()[0];
        log.info(method + " about to change to " + 
            newValue + " on " + jp.getTarget());
    }
}

For those who haven’t done AOP, or who haven’t done AOP in a while, an aspect consists of a pointcut and advice. The advice is what you want to do, and the pointcut is where you want to do it.

In this case, the pointcut is inside the @Before annotation. It states that the pointcut is before every method in the system that begins with set, takes a single argument (two dots would represent zero or more arguments — a single star is one argument) and returns void.

The advice is the trackChange method. Spring calls this method whenever the pointcut applies, and it supplies the JoinPoint argument from AspectJ. The join point provides context, because from it you can get the name of the method being called, its arguments, and a reference to the target object, as shown above.

This aspect logs properties that are about to change. Along with the Spring configuration file (which I’ll get to shortly), it demonstrates an aspect being applied in a very general way.

One of my students, however, had an obvious question. It’s all well and good to print out which set method is being called and on which object, but what would be really useful is to know what the value of the property was before the set method changed it. What’s the current value of the property?

The JoinPoint class doesn’t really have methods to determine that, unfortunately. The javadocs for AspectJ are located at Eclipse, of all places, if you want to take a look.

A friend of mine and I debated how we would go about figuring out the current value. Since we know the name of the setter method being invoked and we have a reference to the current object, some form of reflection and string manipulation would probably do the trick.

That’s when it hit me, though, that the job would be almost trivial in Groovy. Let me show you the answer and then talk about it. Here’s my Groovy aspect.

package mjg.aspects

import java.util.logging.Logger

import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before

@Aspect
class UpdateReporter {
    Logger log = Logger.getLogger(UpdateReporter.class.name)
    
    @Before("execution(void set*(*))")
    void reportOnSet(JoinPoint jp) {
        String method = jp.signature.name
        String property = (method - 'set').toLowerCase()
        def current = jp.target."$property"
        log.info "About to change $property from $current to ${jp.args[0]}"
    }
}

I called the aspect UpdateReporter. It defines the same pointcut as the PropertyChangeTracker. This time, though, it’s easy to figure out the current value of the property. I just subtract set from the name of the method and convert to lowercase, which gives me the property name. Then I invoke the get method by using the standard POGO convention that accessing the property invokes the getter. The string interpolation is just to make sure I evaluate the method rather than treat it as a string property of the class.

I now need a class with some set methods in it, so I made a POJO.

package mjg;

public class POJO {
    private String one;
    private int two;
    private double three;
    
    public String getOne() { return one; }
    public void setOne(String one) { this.one = one; }
    public int getTwo() { return two; }
    public void setTwo(int two) { this.two = two; }
    public double getThree() { return three; }
    public void setThree(double three) { this.three = three; }

    @Override
    public String toString() {
        return "POJO [one=" + one + ", two=" + two + ", three=" + three + "]";
    }
}

Here is the Spring bean configuration file, in XML.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy />

    <bean id="updater" class="mjg.aspects.UpdateReporter" />
    <bean id="tracker" class="mjg.aspects.PropertyChangeTracker" />	 
    <bean id="pojo" class="mjg.POJO" p:one="1" p:two="2" p:three="3"/>	
</beans>

The aop namespace contains the aspectj-autoproxy element, which tells Spring to pay attention to the @Aspect annotations and generate proxies as needed. Spring AOP applies at public method boundaries of Spring-managed beans, so I needed to add the POJO bean to the configuration file as well.

The final piece of the puzzle is to actually call the setter methods on the POJO, which I did with a test case. I used Spring’s JUnit 4 test runner to cache the application context.

(In other cases I use Spock’s Spring capabilities to do the same thing with Spock tests, but that’s another story.)

package mjg;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration("/applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class POJOTest {
    @Autowired
    private POJO pojo;

    @Test
    public void callSetters() {
        pojo.setOne("one");
        pojo.setTwo(22);
        pojo.setThree(333.0);
        assertEquals("one", pojo.getOne());
        assertEquals(22, pojo.getTwo());
        assertEquals(333.0, pojo.getThree(),0.0001);
    }
}

The Spring test runner also injects the POJO into the test, which is convenient. Running the test then prints to the console (cleaned up a bit):

INFO: About to change one from 1 to one
INFO: setOne about to change to one on POJO [one=1, two=2, three=3.0]
INFO: About to change two from 2 to 22
INFO: setTwo about to change to 22 on POJO [one=one, two=2, three=3.0]
INFO: About to change three from 3.0 to 333.0
INFO: setThree about to change to 333.0 on POJO [one=one, two=22, three=3.0]

How cool is that? I love it when I find an application where Groovy makes life much easier than the Java alternatives. I have to admit that I was almost insufferably pleased with myself when I got this to work, though of course it’s all Groovy — and Spring’s very friendly relationship to it — that made it all possible.

By the way, what is that mjg package I keep using? Why, Making Java Groovy, of course. This example is going directly into the chapter on Groovy and Spring, so consider this a bit of a teaser. 🙂

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

8 Responses to Cool Groovy aspect in Spring

  1. Kent says:

    Great details. I’m learning Grails/Groovy – looking forward to your book.

  2. Andre says:

    Trying to run this examplo http://www.grails.org/doc/latest/guide/14.%20Grails%20and%20Spring.html about Grails and Spring AOP but nothing happens. Any clues?

  3. Ken Kousen says:

    Do you mean the AOP aspect at the end of section 14.4? What did you actually implement?

  4. Andre says:

    Exactly! the AOP aspect at the end of section 14.4

    I implemented it hoping to have my first AOP example to work, but nothing happened after a change the birthday of a Person object.

    I also tried your’s example, but couldn’t make a translation to use spring DSL

  5. Andre says:

    When I try to run your example, grails throws the following exception:

    No such property: parentmessagesource for class:
    ERROR context.GrailsContextLoader – Error executing bootstraps: No such property: parentmessagesource for class: org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource
    Possible solutions: parentMessageSource
    groovy.lang.MissingPropertyException: No such property: parentmessagesource for class: org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource
    Possible solutions: parentMessageSource

  6. Ken Kousen says:

    My example didn’t use Grails at all. I did everything with just Groovy and Spring. I haven’t had a chance yet to try out the Grails demo inside of their documentation, but when I do I’ll report on what I find.

    That said, it looks like somehow the application context is missing a message source. I don’t know why that would be the case in a normal Grails app, but I’ll take a look.

  7. Rafael says:

    I’ve just realized if the target object (POJO in this case) is implemented also by groovy the method intercepted is always GroovyObject.getMetaClass().
    Any suggestion to solve it

  8. Sam Lukes says:

    Nice example, I suppose it could be done in java by using some horrible reflection – but that’s great!

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

%d bloggers like this: