Spring 2.5′s Unintrusive Annotation-based Configuration

November 14, 2007 – 22:00 | java | Tags: ,

Annotation-based configuration isn’t new, but Spring 2.5 adds an interesting twist to it by making it possible to use annotations for wiring, yet without being explicitly tied to any Spring classes.

Say we have two classes:

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.beans.factory.annotation.Component;
  3. @Component
  4. public class Psychic {
  5.     private CrystalBall daBall;
  6.     public String tellFate() {
  7.         return daBall.getPrediction();
  8.     }
  9.     public CrystalBall getDaBall() {
  10.         return daBall;
  11.     }
  12.     @Autowired
  13.     public void setDaBall(CrystalBall daBall) {
  14.         this.daBall = daBall;
  15.     }
  16. }
  17. class CrystalBall {
  18.     private String prediction;
  19.     public String getPrediction() {
  20.         return prediction;
  21.     }
  22.     public void setPrediction(String prediction) {
  23.         this.prediction = prediction;
  24.     }
  25. }

In the context file, a simpe one-liner

  1. <context :annotation-config/>

gets the above two beans wired together (or any number of other beans as long as they are properly annotated with the two markers).

Easy, right? Well, the only problem is that those two imports make our otherwise nice domain class depending on some Spring infrastructure. Fortunately, with a little extra config, we can remove this dependency.

First we create our own annotation classes. In this case I simply copied the two Spring ones into my package structure, with the code itself remaining the same. So I won’t bother to post it here. Bottom line is now we have our own sandbox.spring.annotation.Component and sandbox.spring.annotation.Autowired.

So now we replace the Spring annotation classes in those imports with our own versions, and change that one-liner above to:

  1. <bean id="autowiredAnnotationBeanPostProcessor" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"
  2.      p:autowiredAnnotationType="sandbox.spring.annotation.Autowired" />
  3.  
  4. <context :component-scan base-package="sandbox.spring.annotation" annotation-config="false">
  5.     <context :include-filter type="annotation" expression="sandbox.spring.annotation.Component" />
  6.     <context :exclude-filter type="annotation" expression="org.springframework.stereotype.Component" />
  7. </context>

Basically the autowiredAnnotationBeanPostProcessor bean definition defines a customized BeanPostProcessor that looks for our own Autowired annotation instead of the default one. And then the <context:component-scan> element overrides the default Component annotation with our version.

Note that it’s important to have the annotation-config="false" above, for it turns off the default BeanPostProcessors.

Also worth noting is that the extra config is applied to all other annotated beans, so despite it might look overly complicated in this case, it’s really trivial in any serious context where hundreds of beans could benefit from this same piece of config.

Similar to the two annotations discussed above, pretty much all others (such as @Required) can be substituted with customized versions via similar configuration.

I have never been a big fan of annotation-based configuration. One of my biggest concerns – and perhaps the only practical one – is the compile-time coupling it introduces. With this unintrusive way being introduced in Spring 2.5, I find annotation-based configuration a strategy more valid than ever.

Trackback from your site, or follow the comments in RSS.
  1. 2 Responses to “Spring 2.5′s Unintrusive Annotation-based Configuration”

  2. Jing,

    Another option for applying custom annotations is to use Spring’s @Component as a meta-annotation. In other words:

    @Component
    public @interface SomeCustomAnnotation

    That is still non-invasive as far as the actual annotated components are concerned:

    @SomeCustomAnnotation
    public class Whatever { … }

    Obviously it requires a Spring-specific import in the custom annotation definition, but it does simplify configuration (it is no longer necessary to provide the ‘include-filter’ within the ‘component-scan’ element). I also wanted to point out that the ‘component-scan’ element has an attribute for disabling all default filters so that it is not necessary to provide that ‘exclude-filter’ either: use-default-filters=”false”.

    Mark

    By Mark Fisher on Nov 15, 2007

  3. Mark,

    Neat meta-annotation. I completely forgot you could do that. Copying the annotations did look rather silly… :-)

    The exclude/include filters were actually taken from the 2.5 RC1 reference. I guess I need to keep an eye on how it evolves.

    Thanks!

    Jing

    By Jing Xue on Nov 16, 2007

Post a Comment