« Using AspectJ with Spring - part 1 | Main | Effective AspectJ - Understanding type checking for pointcuts and advice »

May 19, 2004

What the teacher said (using AspectJ with Spring part II)

Yesterday I introduced a simple example of a Teacher aspect responding to a playground kiss. Today's objective is to make the teacher's response configurable as a bean property using Spring.

First let's update the Teacher aspect to have a response property. I'm going to use setter-injection for reasons that will become apparent later:

public aspect Teacher { private String response; public void setResponse(String response) { this.response = response; } /** * There are many things that can be kissed, and many possible kissers, * but here we're just interested in Girls kissing Boys. */ pointcut aGirlKissingABoy() : call(* Kissable.kiss()) && this(Girl) && target(Boy); /** * Now we get to decide what the teacher's reaction is to the kiss... */ after() returning : aGirlKissingABoy() { System.out.println(response); } }

Now the tricky bit - how do we convince Spring to configure the Teacher aspect? Spring usually both instantiates and configures bean objects, but aspects in AspectJ are implicitly constructed on point of first reference - so it doesn't work for Spring to simply call the default constructor of Teacher. This is also the reason that we can't use constructor-injection: AspectJ will actually give a compile-time error if you attempt to define a non-zero argument constructor in an aspect. I've played round with various permutations to address this configuration problem, and I don't claim to have arrived at the ideal solution yet, but the following works quite nicely.

<beans> <bean id="girl" class="Girl"> <property name="kissable"><ref bean="boy"/></property> </bean> <bean id="boy" class="Boy"> </bean> <bean id="teacher" class="Teacher" factoryMethod="aspectOf"> <property name="response"><value>we'll have none of that please...</value></property> </bean> <bean id="aspectFactory" class="org.aspectj.springframework.beans.factory.config.AspectBootstrapBean"> <property name="aspects"> <list> <ref bean="teacher"/> </list> </property> </bean> </beans>

Notice that the teacher bean is configured almost exactly like any other bean, except that instead of letting Spring use a default constructor to create an instance of the bean, we ask it to use a factory method instead. In this case the factory method is aspectOf, which is defined by AspectJ on every singleton aspect and returns the aspect instance as created by the AspectJ runtime. I added an second bean into the mix too, the "aspectFactory" bean. Note that the class of this bean refers to a framework class I wrote to aid in the bootstrap process. This bean is configured like any other bean, and has an "aspects" property that should be passed a list of all the (aspect) beans to be used in the program. This has the consequence that loading the aspectFactory bean causes Spring to create and configure all of the aspects we will need.

Here's the updated main program that puts it all together:

public class Main { public static void main(String[] args) throws Exception { XmlBeanFactory beanFactory = new XmlBeanFactory(new FileInputStream("beans.xml")); beanFactory.getBean("aspectFactory"); // causes init and config of all aspects Girl g = (Girl) beanFactory.getBean("girl"); g.kiss(); } }

If you run this program you get the output:

wahay!
we'll have none of that please...


confirming that Spring has indeed configured the teacher aspect correctly. Aspects configured in this way support all of the various property types that Spring offers (references to other beans, lists, maps, constant values, etc.).

There's lots more to talk about - in future posts I'll discuss what happens when you want two Teachers to show up at the same time, the issues surrounding aspect instantiation models other than the default (perthis, pertarget, ...), how to (dynamically) configure pointcut expressions as bean properties, using the Spring configuration to enable and disable aspects, and of course how AspectJ fits in with Spring's own AOP framework.

All of this is new territory, so if you've suggestions for how you'd like to see things work, just let me know...


Ok, I confess, I cheated a little bit. The "factoryMethod" attribute doesn't yet exist in Spring's DTD, but I've been talking a lot with Rod Johnson about all this and it will be coming in the Spring 1.0.3 release. Until then, there's a variation you can use that works with Spring today, but is a little more verbose. It entails a change to the config file I showed you, and it looks like this:

<beans> <bean id="girl" class="Girl"> <property name="kissable"><ref bean="boy"/></property> </bean> <bean id="boy" class="Boy"> </bean> <bean id="teacher" class="org.aspectj.springframework.beans.factory.config.AspectFactoryBean"> <property name="staticMethod"><value>amc.Teacher.aspectOf</value></property> <property name="aspectProperties"> <map> <entry key="response"> <value>we'll have less of that please...</value> </entry> </map> </property> </bean> <bean id="aspectFactory" class="org.aspectj.springframework.beans.factory.config.AspectBootstrapBean"> <property name="aspects"> <list> <ref bean="teacher"/> </list> </property> </bean> </beans>

For the curious, AspectFactoryBean is a little helper class I wrote that extends Spring's MethodInvokingFactoryBean to also configure the bean that it creates.

Posted by adrian at May 19, 2004 10:53 PM [permalink]

Comments

Thank you!

Posted by: Brian McCallister at May 19, 2004 02:57 PM

Good blog. Thanks.

Posted by: jayc100 [TypeKey Profile Page] at June 13, 2007 04:56 PM

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.)


Remember me?