Reducing XML with Spring 2.1 and annotations - No Fluff Just Stuff

Reducing XML with Spring 2.1 and annotations

Posted by: Craig Walls on May 16, 2007

If you're a regular reader of this blog, then you know that I've been spending some time exploring alternatives to Spring's XML configuration. Although I have no specific issues with using XML to configure Spring, I do know that some folks think that XML is a bad thing--therefore to ease their mind, I've taken it upon myself to report about the configuration options available to Spring developers.

In case you missed the memo, Spring 2.1-m1 has been released. Among other things, Spring 2.1-m1 adds some configuration-by-annotation options as an alternative Spring's verbose (and allegedly evil) XML configuration.

To demonstrate these new annotations, I'll revisit the knight example from chapter 1 of Spring in Action, Second Edition (coming to a bookshelf near you in July). If you'd like to follow along at home, you can download the code from here.

(Note: The example code is a Maven-ized project. You don't need Maven to follow along, but if you want the convenience of using my predefined build, you'll need to have Maven 2 installed. When you're ready to compile/run it, just type "mvn install" at the command line.)

Before we get started, let's take a quick look at the current state of the knight example's configuration 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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
          http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
          
  <bean id="quest"
      class="com.springinaction.chapter01.knight.HolyGrailQuest"/>

  <bean id="knight"
      class="com.springinaction.chapter01.knight.KnightOfTheRoundTable">
    <constructor-arg value="Bedivere" />

    <property name="quest" ref="quest" />
  </bean>

  <bean id="minstrel"
      class="com.springinaction.chapter01.knight.Minstrel"/>

  <aop:config>
    <aop:aspect ref="minstrel">

      <aop:pointcut
          id="questPointcut"
          expression="execution(* *.embarkOnQuest(..)) and target(bean)" />

      <aop:before
          method="singBefore"
          pointcut-ref="questPointcut" 
          arg-names="bean" />

      <aop:after-returning
          method="singAfter"
          pointcut-ref="questPointcut" 
          arg-names="bean" />
    </aop:aspect>
  </aop:config>

</beans>

There are only three beans and an aspect definition...not a grossly verbose bit of XML. Nonetheless, as this article concerns configuring Spring without XML, I'll need to gut this configuration file. Here's what I'll work with after removing the unneeded bean and aspect elements:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
          http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
          

</beans>

Wow! That's a lot cleaner. But it's also useless. To make Spring 2.1's annotations work, I'll need to add a tiny bit of XML back to the XML. Specifically, I'll need to put in a <context:component-scan> element:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"

       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-2.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.1.xsd">

  <context:component-scan
      base-package="com.springinaction.chapter01.knight" />

</beans>

(Notice that I also had to add the XML Schema preamble to make the "context" namespace available.)

<context:component-scan> is a new configuration element in the "context" namespace (which, BTW, is also new in Spring 2.1). This modest little element tells Spring to scan the "com.springinaction.chapter01.knight" package (as specified by the base-package attribute) and any subpackage looking for classes that might be annotated with @Component (new in Spring 2.1), @Repository (new in Spring 2.0), or @Aspect (provided by @AspectJ). If it finds any such class, it automatically will register the class as a bean in the Spring application context. So, although <context:component-scan> takes up so very little space in the XML configuration, it could be responsible for configuring dozens, hundreds, or even thousands of beans in Spring.

So where do these annotated classes come from? Well, we have to create them...let's do that now.

Let's add some annotations

Alrighty, then...I had to add a bit of XML, but fortunately, that's almost all of the XML we need. (I'll put in a bit more later for the aspect-oriented stuff.) Now I need to slap a few annotations into the knight example's Java class files. First up is the KnightOfTheRoundTable class:

package com.springinaction.chapter01.knight;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("knight")
public class KnightOfTheRoundTable implements Knight {
  public void embarkOnQuest() {
    quest.embark();
  }
  
  private String name = "Bedivere";
  public void setName(String name) {
    this.name = name;
  }
  
  public String getName() {
    return name;
  }

  private Quest quest;
  @Autowired
  public void setQuest(Quest quest) {
    this.quest = quest;
  }
}

The first of the new annotations I used is @Component. @Component tells Spring that this class should be automatically registered in the Spring context. By default, the class name is used as the bean's ID, but here I've explicitly specified that the bean should have an ID of "knight".

Next, notice that the setQuest() method is annotated with @Autowired. This tells Spring that this setter shold automatically be wired with a bean reference. The autowiring is "byType", so there will need to be a Quest bean in the Spring context. There's not one there now, but I'll add one soon.

However, before we look at the Quest bean, you may be interested in noting that the @Autowired annotation doesn't have to be used with setter methods. In fact, you can use it with any method you want. For example, the following use of @Autowired is perfectly valid:

  @Autowired
  public void assignAQuest(Quest quest) {
    this.quest = quest;
  }

Although it does the work of a setter, assignAQuest() isn't technically a setter method in the JavaBeans sense. Nonetheless, @Autowired has no problem injecting a quest into it through its argument.

Moreover (and perhaps even more interesting), @Autowired doesn't even need a method at all to do its job. You can just as easily use it on a private instance variable:

  @Autowired
  private Quest quest;

Goodbye unnecessary setter methods! (Well, only if you're using these annotations. If you're configuring with Spring XML, you'll still need them.)

Now let's add the Quest bean to the Spring context. Normally I'd do this with a <bean> element in XML. But since I'm using <context:component-scan> I just need to annotate the HolyGrailQuest class with @Component:

package com.springinaction.chapter01.knight;

import org.springframework.stereotype.Component;

@Component
public class HolyGrailQuest implements Quest {
  public void embark() {
    System.out.println("Embarking on the quest for the Holy Grail!");
  }
}

Voila! The "knight" and "quest" beans are now wired in Spring...using annotations instead of XML. We're about ready to add AOP to the mix. But first, let me address any concerns that you may have with auto-wiring.

Explicit wiring with annotations

If you're like me, auto-wiring makes you a bit nervous. It's fine for small examples, but as an application gets larger, auto-wiring can be quite a confusing mess, leaving you wondering how things are wired together.

If explicit wiring is more your style, then Spring has you covered with the @Resource annotation. @Resource is a JSR-250 annotation that lets you explicitly pick out which bean to wire into a property.

For example, to hand-pick a bean named "grailQuest" to wire into the KnightOfTheRoundTable, you can annotate the setter method like this:


  @Resource(name="grailQuest")
  public void setQuest(Quest quest) {
    this.quest = quest;
  }

Now let's turn our attention to AOP with annotations by wiring in the Minstrel (musically inclined logging system of medieval times) aspect.

Spring, AOP, and annotations

To setup the Minstrel aspect, I'll use @AspectJ annotations. Spring's support for @AspectJ annotations isn't new to Spring 2.1. But Spring 2.1 does make it a bit easier, as you'll soon see.

To add AOP support, the first thing I'll do is add one more line of XML to the Spring XML configuration:


<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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-2.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.1.xsd">

  <context:component-scan
      base-package="com.springinaction.chapter01.knight" />

  <aop:aspectj-autoproxy/>

</beans>

<aop:aspectj-autoproxy> was introduced in Spring 2.0 to automatically proxy @AspectJ-annotated classes. But in Spring 2.0, you'd still need to explicitly declare the @Aspectj-annotated classes as beans in XML. Spring 2.1's <context:component-scan> element simplifies things a bit by automatically registering any class that is annotated with @Aspect. Once @Aspect-annotated classes are registered in the Spring context by <context:component-scan>, <aop:aspectj-autoproxy> takes over to create the proxies.

In the XML-oriented configuration, the Minstrel class was a simple POJO with no special interfaces or annotations:

package com.springinaction.chapter01.knight;

import org.apache.log4j.Logger;

public class Minstrel {
  private static final Logger SONG = 
      Logger.getLogger(Minstrel.class);

   public void singBefore(Knight knight) {
    SONG.info("Fa la la; Sir " + knight.getName() +
        " is so brave!");
  }
  
  public void singAfter(Knight knight) {
    SONG.info("Tee-hee-he; Sir " + knight.getName() +
        " did embark on a quest!");
  }
}

In the original version, I used elements in Spring's "aop" namespace to turn Minstrel into an aspect. But for this article, I'm trying to cut down on the XML and exploit the annotations. Therefore, I'm going to tweak Minstrel to use @AspectJ-style annotations:

package com.springinaction.chapter01.knight;

import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class Minstrel {
  private static final Logger SONG = 
    Logger.getLogger(Minstrel.class);
  
  @Pointcut("execution(* com.springinaction.chapter01.knight.Knight.embarkOnQuest(..))")
  public void questEmbarkment() {}
  
  @Before("questEmbarkment() && this(knight)")
  public void singBefore(Knight knight) {
    SONG.info("Fa la la; Sir " + knight.getName() +
    " is so brave!");  
  }
  

  @After("questEmbarkment() && this(knight)")
  public void singAfter(Knight knight) {
    SONG.info("Tee-hee-he; Sir " + knight.getName() +
        " did embark on a quest!");
  }
}

This new Minstrel class is a typical @AspectJ aspect. The annotations applied are:

  • @Aspect indicates that this class is an aspect.
  • @Pointcut defines a pointcut. Notice that there's an empty method that serves as a place to attach the aspect and also provides the name for the pointcut. (Weird, yes...take it up with the AspectJ team.)
  • The @Before and @After annotations indicate the methods to be invoked before and after (respectively) when the pointcut is encountered.

And that's it! I've extracted all of the configuration details out of Spring XML and reworked it as annotations. As a reminder, here's the final version of the 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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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-2.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.1.xsd">

  <context:component-scan
      base-package="com.springinaction.chapter01.knight" />

  <aop:aspectj-autoproxy/>

</beans>

After yanking all of the application-specific bean definitions, I ended up putting back a couple of infrastructural elements. But in the end, there is not nearly as much XML. And, furthermore, as long as all of the application's beans are packaged in com.springinaction.chapter01.knight (or a subpackage), then I probably won't have to add much more XML to the Spring XML configuration.

Conclusion

Spring 2.1-m1 offers another choice for Spring configuration in the form of annotations. Although I still favor Spring XML, I'm happy to see that there are a wealth of configuration options for Spring developers to choose from.

The downside of using annotations in this way, as opposed to XML or even Spring JavaConfig, is that there's not a central place to look to gain an understanding of how application objects are wired together. With Spring's XML configuration, you would only need to view a handful of XML files to get the big picture of a Spring application's structure. Spring IDE and Spring BeanDoc take advantage of this to generate a visual guide to a Spring application's beans. With annotations, however, it's not so cut and dried. The application wiring is spread out across the application's many classes.

In the end, Spring 2.1's annotations are one choice among many for configuring Spring. A wise developer will choose the correct configuration option for their application, whether it be Spring 2.1 annotations, XML, JavaConfig, or a scripted configuration like Groovy or JRuby. Or you may mix-n-match configuration styles in the same application.

Finally, for those of you who may be wondering, Spring in Action, 2E is on its way to being complete. The writing phase is complete and it has been in the production team's hands for a few weeks. We've hit a few snags, but those are clearing up and we're anticipating a July release. Unfortunately, the timing of Spring 2.1 didn't allow me to cover any 2.1 stuff in the book (I'm compiling my examples against Spring 2.0.5). But watch this blog for more interesting Spring 2.1 stuff as I have the opportunity to share it.

Craig Walls

About Craig Walls

Craig Walls is a Principal Engineer, Java Champion, Alexa Champion, and the author of Spring AI in Action, Spring in Action, and Build Talking Apps. He's a zealous promoter of the Spring Framework, speaking frequently at local user groups and conferences and writing about Spring. When he's not slinging code, Craig is planning his next trip to Disney World or Disneyland and spending as much time as he can with his wife, two daughters, 1 bird and 2 dogs.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »