Notes on Spring XML configuration

This post is below my usual quality standards, it’s just bunch of notes that I made while reading “Spring in Action”. Still I publish it mostly for myself but I hope someone may find it helpful too.

Registering and using beans via Spring

Let’s start with simple StdOutNotificationService bean:

package beandemo;

public interface NotificationService {
    void showNotification(String message);
}

public class StdOutNotificationService implements NotificationService {
    @Override
    public void showNotification(String message) {
        System.out.println("NOTIFICATION: " + message);
    }
}

To register it with Spring we must add the following configuration file as a resource to our application:

<?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="notificationService" 
      class="beandemo.StdOutNotificationService" />

</beans>

NOTE: The id attribute of bean element is optional - we can refer to beans either by their name or their type.

Now we may create ApplicationContext from our configuration file and finally use our beans:

public static void main(String[] args) {
   ApplicationContext context =
      new ClassPathXmlApplicationContext(new String[] {
        "beandemo/bean-config.xml"
         // other configuration files
      });

   NotificationService notificationService =
      context.getBean(NotificationService.class);
   // or: context.getBean("notificationService")
   
   notificationService.showNotification("Yay! It works!");
}

Injection using constructor

Let’s start with a simple example ConstructorDemo bean requires two other beans (NotificationService and SystemUserNameProvider) to work:

public class ConstructorDemo {
    private final NotificationService notificationService;
    private final SystemUserNameProvider systemUserNameProvider;
    
    public ConstructorDemo(
            NotificationService notificationService, 
            SystemUserNameProvider systemUserNameProvider) {
        super();
        this.notificationService = notificationService;
        this.systemUserNameProvider = systemUserNameProvider;
    }

    // ...   
}

In this case we should use Spring constructor-arg element to tell Spring how it should resolve constructor arguments:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="...">
   <bean id="notificationService" class="beandemo.StdOutNotificationService" />
   <bean id="systemUserNameProvider" class="beandemo.JdkSystemUserNameProvider" />

   <bean id="constructorDemo" class="beandemo.ConstructorDemo">
      <constructor-arg ref="notificationService" />
      <constructor-arg ref="systemUserNameProvider" />
   </bean>
</beans>

We must provide all constructor arguments otherwise Spring will throw UnsatisfiedDependencyException.

The order of constructor-arg elements is not important because arguments are matched by type. In these rare cases when ambiguity arise we may use name and index attributes of constructor-arg element to specify to which parameter dependency should be bound, for example:

<bean id="someBean" class="beandemo.SomeBean">
   <constructor-arg ref="dependency1" name="parameter1" />
   <constructor-arg ref="dependency2" name="parameter2"  />
</bean>

When providing constructor arguments we are not limited to references to other beans, we may also provide standard Java types like String. For example given bean:

public class ValueBean {
    public ValueBean(String stringValue, Integer intValue, Boolean boolValue) {
        super();
        this.stringValue = stringValue;
        this.intValue = intValue;
        this.boolValue = boolValue;
    }
}

We may register it as follows (Spring will take care of converting strings to apropriate types):

<bean id="prefixSuffix" class="beandemo.ValueBean">
   <constructor-arg value="some string" />
   <constructor-arg value="123" />
   <constructor-arg value="false" />
</bean>

Sometimes we want to set some constructor arguments to null, we may use <null /> element to do exactly that:

<bean class="...">
   <constructor-arg>
      <null />
   </constructor-arg>
   <constructor-arg ref="fooService" />
</bean>

Injection using setters

The most popular industry convention is that all required bean dependencies should be injected via constructor and optional dependencies via setters.

Let’s see how it works on example: FooService bean requires BarService and has optional dependency on BazService. This means that even without BazService FooService will work but some features will not be accessible.

public class FooService {
    private final BarService barService;
    private BazService bazService;
    
    public FooService(BarService barService) {
        super();
        this.barService = barService;
    }

    public void setBazService(BazService bazService) {
        this.bazService = bazService;
    }
    
    public void foo() {
        barService.bar();
        
        if (bazService != null)
            bazService.baz();
    }
}

And here is configuration for our example:

<beans xmlns="...">
   <bean id="fooService" class="beandemo.FooService">
      <constructor-arg ref="barService" />
      <!-- here we provide optional dependency -->
      <property name="bazService" ref="bazService" />
   </bean>
   
   <bean id="barService" class="beandemo.BarService" />
   <bean id="bazService" class="beandemo.BazService" />
</beans>

Property injection can also be used to populate JavaBeans, for example given bean:

public class Configuration {
    private String outputFile;
    private String inputFile;
    private Boolean enableLogging;
 
    public String getOutputFile() {
        return outputFile;
    }
    public void setOutputFile(String outputFile) {
        this.outputFile = outputFile;
    }

    // ...
}
<bean id="configuration" class="beandemo.Configuration">
   <property name="enableLogging" value="false" />
   <property name="inputFile" value="input.txt" />
   <property name="outputFile" value="output.txt" />
</bean>

As with constructor-arg we use ref attribute to refer to other beans and value attribute to provide inline value. We can set property value to null by writing:

<property name="propertyName">
   <null />
</property>

Since writing property may be tiring (at least in case of JavaBeans) there is a shortcut, first we must add XML p namespace to configuration file:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:p="http://www.springframework.org/schema/p"
   ...

Then we may use p:<propertyName>=value syntax in configuration:

<bean id="configuration" class="beandemo.Configuration"
   p:inputFile="input.txt"
   p:outputFile="output.txt"
   p:enableLogging="true">
</bean>

There is also short cut for <property name="" ref="" />, just add -ref to the end of property name e.g. p:bazService-ref="baz".

Injecting collections

Sometime we want to inject not single beans but collections of beans, for example given bean:

public class MessageFilterService {
    private final List<MessageFilter> filters;

    public MessageFilterService(List<MessageFilter> filters) {
        this.filters = filters;
    }
    
    public List<Message> getGoodMessages(List<Message> messages) {
        return messages.stream()
            .filter(msg -> filters.stream()
                        .allMatch(filter -> filter.allow(msg)))
            .collect(toList());
    }
}

public interface MessageFilter {
    boolean allow(Message message);
}

To wire list of selected MessageFilters into MessageFilterService we must use list element:

<bean id="messageSizeMessageFilter"
    class="beandemo.MessageSizeMessageFilter" />
    
<bean id="stopSpanMessageFilter"
    class="beandemo.StopSpamMessageFilter" />
    
<bean id="messageFilterService"
    class="beandemo.MessageFilterService">
    <constructor-arg>
        <list>
            <ref bean="messageSizeMessageFilter" />
            <ref bean="stopSpanMessageFilter" />
        </list>
    </constructor-arg>  
</bean>

To wire list of values we may write:

<bean id="listOfValues" class="beandemo.ListOfValues">
    <constructor-arg>
        <list>
            <value>foo</value>
            <value>bar</value>
            <value>nyu</value>
        </list>
    </constructor-arg>
</bean>

When injecting collections we are not limited to list, we may also inject set, map and props (instance of java.util.Properties):

<constructor-arg name="set">
    <set>
        <value>foo</value>
        <value>bar</value>
        <value>nyu</value>
        <ref bean="beanId" />
    </set>
</constructor-arg>
<constructor-arg name="map">
    <map>
        <entry key="foo" value="bar" />
        <entry key="baz" value-ref="beanId" />
        <entry key-ref="beanId" value="42" />
    </map>
</constructor-arg>
<constructor-arg name="props">
    <props>
        <prop key="foo">foo is great</prop>
        <prop key="bar">bar is great too</prop>
    </props>
</constructor-arg>

Miscellaneous

Bean lifetime

By default Spring tread are registered beans as singletons, to change lifetime of bean use scope attribute:

<bean id="..." class="..." scope="prototype" />

Two most useful Spring scopes in console applications are

  • singleton - only single instance of bean will exists per ApplicationContext
  • prototype - create new bean instance per usage (if we inject bean three times we will get three instances)

Springs also supports request and session scopes, they are mainly used in Spring MVC/Spring Boot applications.

Inline beans

Sometimes we want to create bean just to inject it in one place, instead of writing:

<bean id="barUtil" class="BarUtil" />
<bean id="foo">
  <constructor-arg ref="barUtil" />
</bean>

We may write:

<bean id="foo">
  <constructor-arg>
    <bean class="BarUtil" />
  </constructor-arg>
</bean>
Init and Destroy events

Sometimes we want Spring to call initialization method after bean instance is created, and likewise to call bean cleanup method just before ApplicationContext is destroyed. To specify such methods we may use bean element init-method and destroy-method attributes:

<bean id="foo" class="beandemo.FooService" 
   init-method="init"
   destroy-method="destroy">
 <constructor-arg ref="bar" />
 <property name="bazService" ref="baz" />
</bean>

And a bean:

public class FooService {
    public void init() { ... }
    public void destroy() { ... }
    ...
}
Creating beans using factory method

Given singleton implementation:

public enum MySingleton {
    INSTANCE;
    
    public void doStuff() {
        System.out.println("Singletons are good!");
    }
    
    // factory method just for Spring:
    public static MySingleton getInstance() {
        return MySingleton.INSTANCE;
    }
}

We may register it in Spring as a bean using configuration:

<bean id="mySingleton"
    class="beandemo.MySingleton"
    factory-method="getInstance" /> 

Here we are just telling Spring to create mySingleton bean by invoking method getInstance() on MySingleton class.

marcin-chwedczuk

A Programmer, A Geek, A Human