Easing Configuration with Spring Beans

Handling configuration data in Java isn't as convenient as it could be. The old and popular Properties mechanism has an awkward syntax and you have to take care of type safety yourself. The new Preferences mechanism supports XML but still isn't everything I'd like it to be.

Recently, I discovered an alternative while playing with GridGain: Spring Beans. Despite all the hype over the last years I've never worked with Spring before, so I had some catch-up to do. Spring provides - among many other things - an Inversion of Control (IoC) container, similar in style to packages like PicoContainer/NanoContainer or Hivemind. The main service of these containers is Dependency Injection, a non-intrusive way of obtaining dependencies for objects (data sources are a popular example). In this article, I'm focusing on Spring and the configuration aspect.

Spring provides a simple XML format that we can use for configuration files:

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

  <bean id="config" class="de.mafr.demo.Config">
    <property name="hostname" value="mafr.de"/>
    <property name="port" value="8080"/>
  </bean>

</beans>

In the example, we declare a Java Bean (de.mafr.demo.Config) and assign values to its properties. The Config bean is a POJO that follows the Java Bean conventions and isn't tied to Spring in any way. Here it is:

package de.mafr.demo;

public class Config {
    private String hostname;
    private short port;

    public String getHostname() {
        return hostname;
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public short getPort() {
        return port;
    }

    public void setPort(short port) {
        this.port = port;
    }
}

Note that type conversion is done by the container: The port value is converted to int automatically. This works for all base types and for a few other classes like URL.

Now let's actually read the configuration file and obtain a ready to use Config object:

package de.mafr.demo;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class Main {
    public static void main(String[] args) {
        BeanFactory factory = new XmlBeanFactory(
                new FileSystemResource("config.xml"));

        Config config = (Config) factory.getBean("config", Config.class);

        // use config
    }
}

If there's an error, you are notified immediately by an exception from the constructor. The configuration file can be read from disk directly like in the example above (using FileSystemResource), from the classpath (ClassPathResource), a servlet context (ServletContextResource), from a URL (UrlResource), or other sources.

To get the example up and running, a subset of Spring and a few dependencies are required. If you're using Maven, all you have to do is to add the following dependency to your pom.xml:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>2.0.7</version>
</dependency>

A lot more is possible with Spring Beans. See the official reference manual for all the details.

social