Singleton and prototype beans in Spring framework

Dependency injection in Spring facilitates development. You can simply put @Autowired annotation, followed by visibility tag and object, to can use every object managed by Spring context. But beware, all of these objects are a kind of Spring-managed singleton.

This time we'll discover how Spring does manage defined beans. In the first part we'll describe that and explain the concepts of singleton and prototype scopes. In the second one we'll analyze the dependency between singleton and prototype scopes. It will be followed by a part destined to method injection technique. The next chapter will be dedicated to Spring's code analyze. We'll see where and how the beans are constructed.

Spring beans are the singletons

What does it mean ? Does Spring use singleton design pattern to manage the beans ? Not exactly. Singleton design pattern assumes that they are one and only one instance of given class at the space managed by Java's classloader. In the case of Spring, it's a little bit different. By default, they exist one and only one bean for each given org.springframework.context.ApplicationContext instance. It means that if you have two or more contexts, all managed by the same Java's classloader, you will probably have more than one instance of given bean. The only condition is that this bean must be defined in every context. Because the code is better than the words, let's take a look of following code:

public class MultipleContextes {

  public static void main(String[] args) {
    try {
      // retreive two different contexts
      ApplicationContext firstContext = new FileSystemXmlApplicationContext("/home/bartosz/webapp/src/main/resources/META-INF/applicationContext.xml");
      ApplicationContext secondContext = new FileSystemXmlApplicationContext("/home/bartosz/webapp/src/main/resources/META-INF/applicationContext.xml");

      // compare the objects from different contexts
      ShoppingCart firstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart");
      ShoppingCart secondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart");
      System.out.println("1. Are they the same ? " + (firstShoppingCart == secondShoppingCart));

      // compare the objects from the same context
      ShoppingCart firstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart");
      System.out.println("2. Are they the same ? "+ (firstShoppingCart == firstShoppingCartBis));
    } catch (Exception e) {
      e.printStackTrace();
    }
  } 
}

By executing this code, you should get:

1. Are they the same ? false
2. Are they the same ? true

So as you can see, beans are singletons only for one context. And it's the reason why you shouldn't mix Spring's singleton concept with design pattern with the same name.

But what you should to do if you want to use different instances for one defined bean ? It's quite simple. You should configure this bean as being prototype scoped:


Now, after running previous code, you should see on your screen:

1. Are they the same ? false
2. Are they the same ? false

We know already the difference between two scopes. But in which situations we should use singleton and prototype ? Singleton is adapted to stateless beans, i.e. the beans that have no state. It can be for example a service, DAO or controller. All of them don't have theirs own states. Instead of it, they perform some of operations based on transmitted parameters (as HTTP request parameters). In the other hand, all statefull beans manage some state. It can be, for example, a shopping cart bean, by the way used in our previous sample. If it's a singleton, the products bought by two different customers will be placed at the same object. And if one of customers will want to remove one product, the other customer won't be happy. It's why statefull objects should be prototypes.

Putting prototype inside singleton and vice versa

OK, all seem clear until now. But sometimes a complex situations can occur. The first one is the placement of singleton inside prototype bean. Apparently, they are no danger if injected singleton is really a singleton bean (has no state). Imagine that for our shopping cart we need to inject a product service. This service will only check if product added to shopping cart is on stock. As service has no state and it makes the verification based on object passed in method signature, they are no risk.

In the other side, placing a prototype bean inside singleton one requires more effort. You can't inject prototype bean inside singleton bean by using autowiring techniques as @Autowired annotations. These injections are executed only once, when Spring initializes the singleton bean with all of its dependencies. So it means that in the case of following code, the instance of ShoppingCart will always be the same:

@Controller
public class TestController {
  @Autowired
  private ShoppingCart shoppingCart;

  @RequestMapping(value = "/addProduct/{productName}")
  public String testAdd(@PathVariable(value="productName") String productName) {
    Product product = new Product();
    product.setName(productName);
    this.shoppingCart.addProduct(product);
    LOGGER.debug("ShoppingCart is "+this.shoppingCart);
    return "test";
  }
}

Compile this class and make some URL calls: http://localhost:8080/addProduct/ice%20tea, http://localhost:8080/addProduct/milk. In your logger files you will see a sequence like that:

// after http://localhost:8080/addProduct/ice%20tea
ShoppingCart is ShoppingCart {products: [Product {ice tea}]}
// after http://localhost:8080/addProduct/milk
ShoppingCart is ShoppingCart {products: [Product {ice tea}, Product {milk}]}

To make this case work, you should get the ShoppingCart instance manually, through bean factory:

@Controller
public class TestController {
  @Autowired
  private ApplicationContext context;

  @RequestMapping(value = "/addProduct/{productName}")
  public String testAdd(@PathVariable(value="productName") String productName) {
    Product product = new Product();
    product.setName(productName);

    ShoppingCart shoppingCart = (ShoppingCart) context.getBean("shoppingCart");
    shoppingCart.addProduct(product);
    LOGGER.debug("ShoppingCart is "+shoppingCart);
    return "test";
  }
}

Thanks to that, you'll see in the logs that every call causes the generation of new ShoppingCart instance:

// after http://localhost:8080/addProduct/ice%20tea
ShoppingCart is ShoppingCart {products: [Product {ice tea}]}
// after http://localhost:8080/addProduct/milk
ShoppingCart is ShoppingCart {products: [Product {milk}]}

Method injection

Another way to have new instance on every call is a technique called method injection. It looks like our manually bean retrieving but is more elegant. One bean being context aware (with access to application context) will be in charge of generating prototype beans instances inside singleton beans:

@Service("shoppingCartProvider")
public class ShoppingCartProvider implements ApplicationContextAware {

  private ApplicationContext context;

  @Override
  public void setApplicationContext(ApplicationContext context) throws BeansException {
    this.context = context;
  }

  public ShoppingCart getInstance() {
    return (ShoppingCart) context.getBean("shoppingCart");
  }

}

And after change, the controller looks like:

@Controller
public class TestController {
  @Autowired
  private ShoppingCartProvider shoppingCartProvider;
  
  @RequestMapping(value = "/addProduct/{productName}")
  public String testAdd(@PathVariable(value="productName") String productName) {
    Product product = new Product();
    product.setName(productName);

    ShoppingCart shoppingCart = shoppingCartProvider.getInstance();
    shoppingCart.addProduct(product);
    System.out.println("ShoppingCart is "+shoppingCart);
    return "test";
  }
}

It exists also a property that can be defined inside XML configuration files. This property refers to prototype beans and permits to create new instance at every call. In additionally, it can be simply mixed with more that one bean:

    
        
     
 
    
public abstract class ShoppingCartProvider   {
  public abstract ShoppingCart getInstance();
}

Controller's code remains the same as in the case of provider implementing ApplicationContextAware interface. In fact, the difference is placed at provider's level and its bean definition. The definition contains a tag lookup-method. It indicates which method must be used to get new instance of the bean specified in bean attribute. In our case, we are looking for new ShoppingCart's instance by calling getInstance method of ShoppingCartProvider class. Note that both class and method can be abstract. By making that, you let Spring to generate sublcass that will implement the method and return desired bean. If this method isn't abstract, Spring will override it.

Bean classes in Spring

Singletons are mainly present in org.springframework.beans and org.springframework.context packages. First, take a look on BeanFactory interaface from the beans package. It contains two interesting methods which can be used to determine if a bean is singleton or prototype:
- boolean isSingleton(String name) throws NoSuchBeanDefinitionException
- boolean isPrototype(String name) throws NoSuchBeanDefinitionException

Next, we should look inside AbstractFactoryBean, defined in the code comment as a "simple template superclass for FactoryBean implementations". It contains an implementation for getObject method that returns singleton or creates prototype bean. The prototype and singletons are made in different time stages through createInstance method.

Another interesting point is the interface BeanDefinition. As its name indicates, it defines a bean properties, such as: scope, class name, factory method name, properties or constructor arguments.

To see where the beans are initialized, we need to jump into context package and, more exactly, into AbstractApplicationContext class. On its public void refresh() throws BeansException, IllegalStateException we can find some fragments about beans creation, inter alia:
- protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory): all beans implementing org.springframework.beans.factory.config .BeanFactoryPostProcessor interface are initialized and invoked. These beans type is allowed to modify properties or constructor arguments of another beans. Note however that only beans definitions can be modified at this stage. "Normal" beans instances are not yet created. This feature is more exposed on article about bean factory post processor beans in Spring.
- protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory): here the context instantiates and invokes all beans implementing org.springframework.beans.factory.config.BeanPostProcessor interface. The beans with this interface contain the callbacks that can be invoked before or after other beans initialization. You can find more about this subject on article consacred to bean post processors in Spring.
- protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory): mainly calls preInstantiateSingletons method defined in org.springframework.beans.factory.config.ConfigurableListableBeanFactory interface. The objectif of this method is to instantiate all beans defined as non-lazy loaded. If sometimes at application context loading you meet an BeansException exceptions, it probably comes from this method. It throws BeansException when a bean can't be created.

This time we discovered the beans world and was the differences between singleton and prototype scopes. The first one creates exactly one object per container while the second makes a new bean object at every request. Both can work together, but prototypes can't be resolved through @Autowired or another injection techniques. They should be generated with getBean() methods or method lookup. The last artcile shown where we can find the information about beans and theirs initialization.


If you liked it, you should read:

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!