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:
[sourcecode language=”java”]
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());
}
}
[/sourcecode]
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.
[sourcecode language=”groovy”]
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]}"
}
}
[/sourcecode]
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.
[sourcecode language=”java”]
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 + "]";
}
}
[/sourcecode]
Here is the Spring bean configuration file, in XML.
[sourcecode language=”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>
[/sourcecode]
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.)
[sourcecode language=”java”]
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);
}
}
[/sourcecode]
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. 🙂
Leave a Reply