`
happmaoo
  • 浏览: 4309469 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

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

阅读更多
http://jroller.com/comments/colyer/Weblog/what_the_teacher_said_using

<!--StartFragment -->

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.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics