Spring Basics: Wiring Beans with XML Configuration

If you have to work with legacy Spring applications, chances are you will have know how XML-based configuration works. Although Java configuration is preferred for new applications, sometimes you just don’t have a choice.

The Code

You can find the code from this blog post on GitLab.

Dependencies

The only dependency you need to get a Spring container running is spring-context. Add in Maven:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

Declaring Beans in XML

The following class will become a bean:

package com.relentlesscoding.wirebeans;

import java.util.List;

public class Running implements Habit {
    private final String name = "Running";
    private final String description = "Run 10 km every day";
    private List<Streak> streaks;

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public List<Streak> getStreaks() {
        return streaks;
    }

    public void setStreaks(List<Streak> streaks) {
        this.streaks = streaks;
    }

    public void addStreak(Streak streak) {
        streaks.add(streak);
    }
}

Create an XML file, give it any name (I would call it applicationContext.xml) and place it in your src/main/resources.

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd ">

    <bean id="running" class="com.relentlesscoding.wirebeans.Running"/>
</beans>

This would wire a bean with the id running that is of type Running (which is a Habit). You can see why this is much more of a hassle to set up than using a simple @Component annotation on a class or a @Bean annotation in a @Configuration class. Those XML namespaces are nasty, but luckily most IDEs will help you with them.

Injecting Beans

To inject dependencies, we have two choices:

  • Constructor injection
  • Setter injection

Unlike with Java configuration, we cannot insert into fields when using XML-based configuration.

Constructor Injection

Suppose we want to insert one bean into another bean, for instance a HabitRepository that persists habits to the database into a HabitService. By using the <constructor-arg /> element and the ref property, we can accomplish this:

<bean id="habitRepository" class="com.relentlesscoding.wirebeans.HabitRepository" />

<bean id="habitService" class="com.relentlesscoding.wirebeans.HabitService">
    <constructor-arg ref="habitRepository" />
</bean>

If we wanted to pass more arguments to the HabitService constructor, we must keep on eye on the order: it must be the same as the order in which they are declared in the class.

To ease working with constructor arguments and as a way to curtail the verbosity of the XML configuration, Spring offers the c XML namespace to help wire beans without the need to create a sub-element <constructor-arg />:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="currentTime" class="java.time.LocalDate" factory-method="now" />

    <bean id="streak"
        class="com.relentlesscoding.wirebeans.PositiveStreak"
        c:startTime-ref="currentTime" />

We define a bean of type java.time.LocalDate and we use the static factory method now() to get an instance of it. We then use the c namespace to pass it to the constructor of our PositiveStreak bean. c:startTime-ref="currentTime" should be read as: pass the reference to the bean with id currentTime to the constructor argument that has the name startTime.

So we can reference constructor arguments by name. We can also reference them by position. c:_0-ref="currentTime" would do the exact same thing. XML does not allow a digit as the first character of an attribute, so we have to use an underscore. If there is only a single argument to the constructor, we can even use the shorthand c:_-ref="currentTime". I would not want to promote this as readable, but it’s good to know it exists and might be used in the wild.

Read more about the c namespace here.

Setter Injection

To use setter injection with XML-based configuration, you use the <property> element. If you look back at the Running class above, you see it has a method setStreak that takes a List<Streak>:

<bean id="startTime" class="java.time.LocalDate" factory-method="now" />

<bean id="streak"
    class="com.relentlesscoding.wirebeans.PositiveStreak"
    c:_0-ref="startTime" />

<bean id="running" class="com.relentlesscoding.wirebeans.Running">
    <property name="streaks">
        <list>
            <ref bean="streak" />
        </list>
    </property>
</bean>

The <property> element has an attribute name that refers to the field name of the bean being set. In the current case, class Running has a field named streaks. As a child element of <property> we define a list of Streak references.

For a list of literal String values, this would have looked like:

<property name="listOfStrings">
    <list>
        <value>string value 1</value>
        <value>string value 1</value>
        <value><null/></value>
    </list>
</property>

The list is wired with literal Strings and even a literal null.

Spring also provides the p namespace to make this more convenient (less verbose):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="streak"
        class="com.relentlesscoding.wirebeans.PositiveStreak"
        c:_0-ref="startTime" />

    <bean id="running"
        class="com.relentlesscoding.wirebeans.Running"
        p:streaks-ref="streak" />
</beans>

The usage of the p namespace is a lot like that of the c namespace. In this case, p:streaks-ref="streak" tells spring to wire a property named streaks with the bean that is referenced by the id streak. Now, the property streaks takes a List. If we pass only a single element to that list, the current syntax works and Spring will happily insert the single reference to streak into a List for us and pass that to the setter method. If we want to pass more than one element in a list, however, we have to create the list separately first, and then pass the id of that reference to the p property:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="firstStreak" class="com.relentlesscoding.wirebeans.PositiveStreak" c:_0-ref="startTime"/>
    <bean id="secondStreak" class="com.relentlesscoding.wirebeans.PositiveStreak" c:_0-ref="startTime"/>

    <util:list id="myRunningStreak">
        <ref bean="firstStreak"/>
        <ref bean="secondStreak"/>
    </util:list>

    <bean id="running" class="com.relentlesscoding.wirebeans.Running" p:streaks-ref="myRunningStreak"/>
</beans>

We need to add the util namespace and the location of the util schema definition to get this to work. You see that the XML becomes quite verbose the more you try to do with it. The util namespace allows us to create collections of literal values or beans. These collections can then be referenced by their id.

More information:

Taking the App for a Test Ride

package com.relentlesscoding.wirebeans;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class HabitTest {

    @Autowired
    Habit runningHabit;

    @Test
    public void runningHabitIsNotNull() {
        Assert.assertNotNull(runningHabit);
    }

    @Test
    public void runningHabitHasSingleStreak() {
        Assert.assertEquals(2, runningHabit.getStreaks().size());
    }
}

To run integration tests where the Spring context is available, you need the following dependency:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
    <scope>test</scope>
</dependency>

spring-test contains the SpringRunner JUnit runner, and the @ContextConfiguration that will tell Spring where to look for the application context that contains the beans that need to be wired. In this case, we tell it to look at the applicationContext.xml that we put in src/main/resources, so we can reference it by looking at the root of our classpath with the attribute locations = "classpath:applicationContext.xml".

Leave a Reply

Your email address will not be published. Required fields are marked *