Tutorial:

Spring Without XML: The Basics of Spring Annotations vs. Spring XML Files

By Jay Tee

TheServerSide.com

This discussion can be read independently of the previous tutorial on Spring 3.0. However, if you would like to learn how to configure a simple development environment for Spring 3.0, and use that environment to test the code in this example, you might want to check it out:

Previous Spring 3.0 Tutorial: Setting Up and Verifying a Spring 3 Development & Runtime Environment

Spring Without XML: Annotations vs. XML Files

You can watch this tutorial as a video CBT here: Spring3 Without XML

Sometimes I really wonder why anyone would use an XML file to configure their Spring application. It just seems to me that using an XML file makes easy things hard, and hard things exceptionally complicated. Using a simple Java file for maintaining Spring configuration data always seems so…well…simple.

I'm currently working on a Rock-Paper-Scissors game application, and one of the beans that I need to have the Spring container feed me is is something I call a GameSummary bean. It's basically a data object, with properties representing the clientChoice, the serverChoice, the game result, and finally, the date and time at which the Rock-Paper-Scissors game was played. I'm going to use this bean to help demonstrate the various ways that the Spring 3.0 Inversion of Control (IoC) container can be configured to spit out JavaBeans to your applications, both through Spring annotations, and through the Spring XML configuration file.

So, without any further adue, here's the POJO that will encapsulate all of this delicious information will be named GameSummary.

The GameSummary Bean


package com.mcnz.spring;
public class GameSummary {

   private String clientChoice, serverChoice, result;
   private java.util.Date date = null;

    String[] choices = {"rock", "paper", "scissors"};
   String[] results = {"win", "lose", "tie"};

    public GameSummary(){}

    public String getClientChoice() { return clientChoice; }
   public void setClientChoice(String clientChoice) {
     this.clientChoice = clientChoice;
   }
   public String getServerChoice() { return serverChoice; }
   public void setServerChoice(String serverChoice) {
     this.serverChoice = serverChoice;
   }
   public String getResult() { return result; }
   public void setResult(String result) {
     this.result = result;
   }
   public java.util.Date getDate() { return date; }
   public void setDate(java.util.Date date) {
     this.date = date;
   }
   public String toString() {
     return clientChoice +
           ":" + serverChoice +
                 ":" + result + ":" + date;
   }

}


The SumRunner Class

 To compare Spring with and Spring without XML, I’m going to create a runnable class named SumRunner that goes through the steps of obtaining an instance of the GameSummary bean, both by using XML, and also by using Spring without XML. Here’s how the SumRunner looks. (Note that since we are yet to create the SummaryConfig.class, and the summary.xml file, if we attempted to test this code, we would end up with all sorts of esoteric runtime exceptions.)


package com.mcnz.spring;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.*;

public class SumRunner {

  public static void main(String args[]){

   /* Spring IoC Without XML */
    AnnotationConfigApplicationContext context
        = new AnnotationConfigApplicationContext(SummaryConfig.class);
    GameSummary gsA
        = context.getBean("gameSummary", GameSummary.class);

    /* Spring IoC with XML */
    Resource resource = new ClassPathResource("summary.xml");
    BeanFactory beanFactory = new XmlBeanFactory(resource);
    GameSummary gsX=(GameSummary)beanFactory.getBean("gameSummary");

    /* Should essentially print out the same data for both. */
    System.out.println(gsA);
    System.out.println(gsX);
 }

}


The SummaryConfig Class

 The AnnotationConfigApplicationContext, from here on in to be affectionately knowns as ACAC or context, looks for a Java class named SummaryConfig that will contain all of the configuration data that the Spring container needs. Here’s what our special SummaryConfig class will look like:

 


package com.mcnz.spring;

import org.springframework.context.annotation.*;
@Configuration
public class SummaryConfig {
 @Bean
 public GameSummary gameSummary() { return new GameSummary(); }
}


Notice that it has a method whose name matches the first parameter passed to the context’s getBean method: gameSummary.


To me, using a POJO to configure the Spring container is very natural. When executed, the client program SumRunner simply asks Spring for an instance of a GameSummary with the getBean call; with the SummaryConfig file we can see quite vividly how and where that GameSummary instance is created and subsequently returned back to the calling program. This should be easy pickings for any Java developer.


The Spring XML Configuration File: summary.xml


For the XML portion of the SumRunner to execute propertly, we need to add the following file, named summary.xml, to the Java runtime classpath.


<?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-3.0.xsd"
>

  <bean id="gameSummary" class="com.mcnz.spring.GameSummary" />

</beans>

 


If you are following along from theConfiguring Your Environment for Spring Developmenton setting up a Spring 3.0 development envioronment, you can compile your code by executing the following javac command from the C:\_mycode folder:

C:\_easyspring> C:\_jdk1.6\bin\javac -classpath "C:\_springlib\*"  C:\_easyspring\com\mcnz\spring\*.java

To run the code, you would execute the following java command:

C:\_easyspring> C:\_jdk1.6\bin\java -classpath "C:\_springlib\*";C:\_easyspring com.mcnz.spring.SumRunner

Previous Tutorial: Configuring a Spring 3.0 Development Environment


With these four pieces of the puzzle saved and compiled, you’re ready to run your application, to which you will see the following, very dazzling, console output:

null:null:null:null
null:null:null:null

Hey, I never said the output was going to be exciting! All our SumRunner application did was get two GameSummary instances back from Spring, neither of which had any properties initialized. So, the output isn’t glamourous, but it does prove that everything works, and that the two methods generated exactly the same results.

So, what do you think? Is XML evil? Is configuring Spring with Java code instead of XML the best thing since V10, 8.3 litre car engines? At this point, there’s probably not all that much of a difference to discern.

Setting Bean Properties

Now, let’s say that for some reason, the server keeps picking rock, and the client keeps picking paper, and that it happens SO OFTEN that our container should have some sort of mechanism that creates an appropriately initialized bean, just for this specific occasion. How would we do it?
Well, when using a POJO to maintain our configuration, we can just add a new method to our SummaryConfig class that returns a GameSummary instance with the pertinent properties already initialized. Here’s how we’d do it inside of a configuration class ( *Notice that the method is named clientWinsWithPaper()* ):


package com.mcnz.spring;
import org.springframework.context.annotation.*;
@Configuration
public class SummaryConfig {  
 @Bean
 public GameSummary clientWinsWithPaper() {
   GameSummary gs = new GameSummary();

    gs.setClientChoice("paper");
    gs.setServerChoice("rock");
    gs.setResult("win");


    return gs;
 }
}


Setting Bean Properties in the Spring XML File

To set the clientChoice, serverChoice and result properties by using an XML file, here’s how it would look:


<?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-3.0.xsd">
 <bean id="clientWinsWithPaper" class="com.mcnz.spring.GameSummary" >

   <property name="clientChoice" value="paper"/>
   <property name="serverChoice" value="rock"/>
   <property name="result" value="win"/>

   </bean>
</beans>


Since the bean id and the method name in the SummaryConfig class have changed to clientWinsWithPaper, the getBean method calls in the SumRunner must also be correspondingly updated:



package com.mcnz.spring;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.*;
public class SumRunner {

 public static void main(String args[]){
   /* Spring IoC Without XML */
   AnnotationConfigApplicationContext context
          = new AnnotationConfigApplicationContext(SummaryConfig.class);
   GameSummary gsA
          = context.getBean("clientWinsWithPaper", GameSummary.class);
   /* Spring IoC with XML */
   Resource resource = new ClassPathResource("summary.xml");
   BeanFactory beanFactory = new XmlBeanFactory(resource);

 GameSummary gsX
          = (GameSummary)beanFactory.getBean("clientWinsWithPaper");
   System.out.println(gsA);
   System.out.println(gsX);
 }
}


When you actually run the SumRunner class, you will obtain the following output, demonstrating that both the Spring XML configuration, and Spring annotation configuration, generated exactly the same bean:

paper:rock:win:null
paper:rock:win:null

Both syntaxes, whether a property is set in a Java class that is decorated with the @Configuration annotation, or whether it is set in the XML file, will achieve the same result. But which one is better?
Personally, I hate XML, and I much prefer doing configurations in your Java code. With the Java code, you get design-time checking. If you spell ‘setResult’ wrong in the Java file, your compiler will tell you. If you spell ‘result’ wrong in the XML file, you won’t get any error messages until you go and run your program, and then you have to start hunting around in a non-type-checked XML file for your error. I much prefer the way the Java file works in this situation.

 

Recommended Books for Learning Spring

Spring in Action  ~ Craig Walls
Spring Recipes: A Problem-Solution Approach  ~ Gary Mak
Professional Java Development with the Spring Framework ~ Rod Johnson PhD
Pro Java EE Spring Patterns: Best Practices and Design Strategies ~ Dhrubojyoti Kayal

24 May 2010