Thursday, 7 July 2011

Spring Bean Scopes: Singleton and Prototype

Spring defines two bean scopes in an application’s context: singleton and prototype. A singleton scoped bean is one that obeys the rules of the Singleton Pattern: ie. there is only ever one instance of your bean and this is the default when Spring creates its beans. On the other hand, when a bean is marked as a prototype, then for every call to getBean(...) Spring will create a new instance especially for you.

This can be demonstrated using the following code. In this scenario I’ve created a RandomNumberBean class that generates a random number when it’s instantiated saves the number in an instance variable.

public class RandomNumberBean {

 
private static Random rand;

 
private final int value;

 
public RandomNumberBean() {

   
rand = new Random();
   
// Generate a number up to 50
   
value = rand.nextInt(50);
 
}

 
@Override
 
public String toString() {

   
StringBuilder sb = new StringBuilder("\nRandomNumberBean value: " + value);
   
return sb.toString();
 
}
}

I’ve then created a simple XML config file with two entries for my RandomNumberBean class: one with ‘singleton’ scope and one with ‘prototype’ scope.

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

 <bean id="MySingleton" class="example_1_bean_type.RandomNumberBean" scope="singleton"/>
 
 <!--
 'singleton' scope is the default scope. The line above is equivalent to writing: 
 <bean id="MySingleton" class="example_1_bean_type.RandomNumberBean" />
  -->
 
 <bean id="MyPrototype" class="example_1_bean_type.RandomNumberBean" scope="prototype"/>
</beans>

I’ve then got some client code that calls getBean(...) to get hold of each of the RandomNumberBean beans four times and print out its value.

    ApplicationContext ctx = new FileSystemXmlApplicationContext(
       
Constants.PATH_TO_RESOURCES + "BeanType.xml");

   
for (int i = 0; i < 4; i++) {
     
RandomNumberBean instance = ctx.getBean("MySingleton", RandomNumberBean.class);
      System.out.println
("Singleton " + i + " Value: " + instance.toString());
   
}

   
for (int i = 0; i < 4; i++) {
     
RandomNumberBean instance = ctx.getBean("MyPrototype", RandomNumberBean.class);
      System.out.println
("Prototype " + i + " Value: " + instance.toString());
   
}

From the results, you can see that the singleton prints out the same value each time (22) and the prototype prints of a sequence of different values:

Singleton 0 Value: 
RandomNumberBean value: 22
Singleton 1 Value: 
RandomNumberBean value: 22
Singleton 2 Value: 
RandomNumberBean value: 22
Singleton 3 Value: 
RandomNumberBean value: 22
Prototype 0 Value: 
RandomNumberBean value: 16
Prototype 1 Value: 
RandomNumberBean value: 24
Prototype 2 Value: 
RandomNumberBean value: 32
Prototype 3 Value: 
RandomNumberBean value: 49

Obviously, when using singletons, you have to be aware of the usual threading issues as different threads will access the same instance variables. There are a couple of simple strategies you can use to avoid these kinds of problems including:
  • Don’t use instance variables.
  • Make your bean immutable by declaring your instance variables final and using constructor args.
  • Use ThreadLocal to create a set of instant variables for every thread.

No comments: