Spring Bean Scoping Examples

0

When spring context XML is loaded, a bean definition is created for each bean declared in the XML.

If the bean is marked for eager instantiation, for example, using the beans level attribute default-lazy-init=true or the bean level attribute lazy-init=true then the bean will be instantiated right after its bean definition is created.

Spring also allows you to control the bean creation using the attribute scope. If an eagerly instantiated bean is declared scoped at ‘singleton’ then whenever the bean is requested by the getBean(beanId) method, the same instance will be returned.

Suppose the same bean is scoped at ‘prototype’, a unique bean instance is returned whenever getBean(beanId) method is called.

In this article, we will look into the various scopes supported by spring with some examples.

Valid Scopes in Spring

In Spring 1.x, singleton and prototype were the only two valid bean scopes so the scope was specified using singleton attribute set to true or false. From 2.0 on wards, spring supports a variety of scopes which is why it introduced a new attribute scope instead of the singleton attribute.

Let’s review all the available scopes.

  1. singleton – Creates a single bean instance per spring context
  2. prototyp – Creates a new bean instance each time when getBean(beanId) is called.
  3. request – Creates a single bean instance per HTTP request; only valid in the context of a web application
  4. session – Creates a single bean instance per HTTP session; only valid in the context of a web application
  5. globalSession – Creates a single bean instance per global HTTP session; only valid in the context of a portal application

Before we get to the examples, let’s first understand at what stage spring converts a bean definition into a bean.

Bean Creation

When you declare a bean in the configuration file, you are only defining a template for bean creation, the template is represented by BeanDefinition class. A template is not an actual bean instance. You can configure in spring whether you want the beans to be created eagerly right after the bean definition is created or lazily when you call getBean(). See lazy-init examples if you want to know more about lazy-init attribute.

In all our examples, we will use the below bean. We have structured the bean such a way that we know when the bean is created and also shed some light about the scope and its behavior.
Few things to note about the bean:

  1. We will be using this bean in more than one bean definition so we made the bean implement BeanNameAware. This way we know which bean definition the instance represents.
  2. The bean has three state variables – a long value which we will increment whenever toString() is called, bean created date time, and a list of string items. We can add an item using addItem(item)

SpringScopeBean:

package com.javarticles.web;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import org.springframework.beans.factory.BeanNameAware;

public class SpringScopeBean implements BeanNameAware {
    private AtomicLong value = new AtomicLong(0);
    
    private static final DateFormat dateFormat = new SimpleDateFormat(
            "HH:MM:SS");
    
    private String beanCreatedTime;
    
    private String beanName;
    
    private List<String> items = new ArrayList<String>();

    public SpringScopeBean() {
        value.incrementAndGet();
        beanCreatedTime = dateFormat.format(new Date());        
        System.out.println("SpringScopeBean constructor called");
    }

    public String toString() {
        return beanName + " created at " + beanCreatedTime + ", no. of times "
                + beanName + " is called(" + value.getAndIncrement() + ")";
    }

    public void setBeanName(String name) {
        beanName = name;
    }

    public void addItem(String item) {
        items.add(item);
    }

    public List<String> getItems() {
        return items;
    }
}

The first bean definition is of singleton scope. Attribute default-lazy-init is set to true so that it is lazily created when getBean(beanID) is called.

singletonApplicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
	default-lazy-init="true">
	<bean id="singletonBean" class="com.javarticles.web.SpringScopeBean" scope="singleton"/>
</beans>

In below example, we load the context XML and then get the bean.

SpringSingletonBeanExample:

package com.javarticles.web;

import java.io.IOException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringLazySingletonExample {
    public static void main(String[] args) throws IOException {
        System.out.println("Load application context");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "singletonApplicationContext.xml");
        try {
            System.out.println("Get SpringScopeBean bean");
            SpringScopeBean singletonBean = (SpringScopeBean) context
                    .getBean("singletonBean");
            System.out.println(singletonBean);
        } finally {
            context.close();
        }
    }
}

If you note, the bean is created only after the getBean("singletonBean") is called.

Output:

Load application context
Get SpringScopeBean bean
SpringScopeBean constructor called
singletonBean created at 21:06:749, no. of times singletonBean is called(1)

Singleton Scope

Spring will decide which bean instance should be returned according to the bean scope. By default, its scope is ‘singleton’ which means Spring creates exactly one instance for each bean declared in the spring container, and this instance will be shared in the scope of the entire container. This unique bean instance will be returned for all subsequent calls to getBean(beanId).

In the below example, we get the singleton scoped bean and add item ‘one’. We once again retrieve the bean from spring container and this time add ‘two’. Next, we print all the items added. We see both ‘one’ and ‘two’ in there which means the second call to geBean() returned as the same as instance as the first call.

SpringSingletonExample:

package com.javarticles.web;

import java.io.IOException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringSingletonExample {
    public static void main(String[] args) throws IOException {
        System.out.println("Load application context");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        try {
            System.out.println("Get SpringScopeBean bean");
            SpringScopeBean singletonBean = (SpringScopeBean) context
                    .getBean("singletonBean");
            
            singletonBean.addItem("one");
            System.out.println(singletonBean.getItems());
            
            singletonBean = (SpringScopeBean) context
                    .getBean("singletonBean");
            singletonBean.addItem("two");
            
            System.out.println(singletonBean.getItems());
        } finally {
            context.close();
        }
    }
}

Output:

Load application context
Get SpringScopeBean bean
SpringScopeBean constructor called
[one][one, two]

Prototype Scope

Suppose you expect each call to getBean() to return a new instance of the bean then you should change the scope of the bean to ‘prototype’.
Let’s declare a bean with prototype scope.

prototypeApplicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
	default-lazy-init="true">
	<bean id="prototypeBean" class="com.javarticles.web.SpringScopeBean" scope="prototype"/>
</beans>

We will now revisit the item example. Since the bean is scoped as prototype, we should be getting a new instance for each getBean() call thus, the second bean instance shouldn’t be seeing item ‘one’ in its list.

SpringPrototypeExample:

package com.javarticles.web;

import java.io.IOException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringPrototypeExample {
    public static void main(String[] args) throws IOException {
        System.out.println("Load application context");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "prototypeApplicationContext.xml");
        try {   
            System.out.println("Get Prototype bean");
            SpringScopeBean prototypeBean = (SpringScopeBean) context.getBean("prototypeBean");
            prototypeBean.addItem("one");
            System.out.println(prototypeBean.getItems());
            
            prototypeBean = (SpringScopeBean) context
                    .getBean("prototypeBean");
            prototypeBean.addItem("two");
            System.out.println(prototypeBean.getItems());
        } finally {
            context.close();
        }
    }
}

As you can see, the constructor is called twice at each getBean() call.
Output:

Load application context
Get Prototype bean
SpringScopeBean constructor called
[one]SpringScopeBean constructor called
[two]

Session and Request Scopes

The next two scopes session and prototype are valid only in the context of a web application. Let’s write a mini web application so we can understand both these scopes.

First create web.xml.

  1. Make sure you specify the spring context XML location in parameter contextConfigLocation
  2. You also need to specify the listener classes ContextLoaderListener and RequestContextListener

web.xml:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
	<display-name>Spring Scoping Example</display-name>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath*:/webApplicationContext.xml,
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>
</web-app>

Let’s look at the spring context XML. If you note we have used the same bean class to create beans with different scopes.

webApplicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
	default-lazy-init="true">
	<context:annotation-config />
	<bean id="springSessionBean" class="com.javarticles.web.SpringScopeBean" scope="session"/>
	<bean id="springRequestBean" class="com.javarticles.web.SpringScopeBean" scope="request"/>
	<bean id="springSingletonBean" class="com.javarticles.web.SpringScopeBean" scope="singleton"/>
	<bean id="springPrototypeBean" class="com.javarticles.web.SpringScopeBean" scope="prototype"/>
</beans>

Now let’s look at the main page. In the main entry page index.jsp we simply get the bean and print it so we know its creation time and number of times toString() method on the instance is called.

index.jsp:

<%@page import="org.springframework.web.context.support.WebApplicationContextUtils, com.javarticles.web.SpringScopeBean" %>
<html>
<body>
<h2>
<%
    SpringScopeBean sessionBean = (SpringScopeBean) WebApplicationContextUtils.getWebApplicationContext(application).getBean("springSessionBean");
    out.println("Session Scoped Bean" + sessionBean.toString());
    
    SpringScopeBean requestBean = (SpringScopeBean) WebApplicationContextUtils.getWebApplicationContext(application).getBean("springRequestBean");
    out.println("Request Scoped Bean" + requestBean.toString());
    
    SpringScopeBean singletonBean = (SpringScopeBean) WebApplicationContextUtils.getWebApplicationContext(application).getBean("springSingletonBean");
    out.println("Singleton Scoped Bean" + singletonBean.toString());
    
    SpringScopeBean prototypeBean = (SpringScopeBean) WebApplicationContextUtils.getWebApplicationContext(application).getBean("springPrototypeBean");
    out.println("Prototype Scoped Bean" + prototypeBean.toString());
%>
</h2>
</body>
</html>

Right Click on project and click on ‘Run on server’.

Output:

Session Scoped BeanspringSessionBean created at 23:06:406, no. of times springSessionBean is called(1) 
Request Scoped BeanspringRequestBean created at 23:06:424, no. of times springRequestBean is called(1) 
Singleton Scoped BeanspringSingletonBean created at 23:06:424, no. of times springSingletonBean is called(1) 
Prototype Scoped BeanspringPrototypeBean created at 23:06:425, no. of times springPrototypeBean is called(1) 

Now refresh the screen, you get a different output.
Note that the session scoped bean is of the same instance so toString() gets called twice which is why the called number shows 2.
Also note request scoped bean is of a new instance whenever you refresh. You can see a different created time than that of the first request.
Singleton scoped bean is of the same instance as the first one only the toString() got called twice, the call number shows 2.
Prototype scoped bean is of a new instance whenever you refresh. You can see a different created time for each request.

What is the difference between request and prototype scopes?

Within the same web request, if the scope is ‘request’, call to getBean(beanID) will return us the same instance whereas if it is prototype scope, you will get a new instance for each call.

Output:

Session Scoped BeanspringSessionBean created at 23:06:406, no. of times springSessionBean is called(2) 
Request Scoped BeanspringRequestBean created at 23:06:819, no. of times springRequestBean is called(1) 
Singleton Scoped BeanspringSingletonBean created at 23:06:424, no. of times springSingletonBean is called(2) 
Prototype Scoped BeanspringPrototypeBean created at 23:06:820, no. of times springPrototypeBean is called(1) 

What is the difference between session and singleton scopes?

Open the URL ‘http://localhost:8080/springWebappExample/’ in a different browser. You will get a new instance for the session scoped bean. You can verify the time creation is different from the above one whereas the singleton bean time creation is still same as it is application scoped.

Output:

Session Scoped BeanspringSessionBean created at 23:06:430, no. of times springSessionBean is called(1) 
Request Scoped BeanspringRequestBean created at 23:06:430, no. of times springRequestBean is called(1) 
Singleton Scoped BeanspringSingletonBean created at 23:06:424, no. of times springSingletonBean is called(3) 
Prototype Scoped BeanspringPrototypeBean created at 23:06:430, no. of times springPrototypeBean is called(1)

Download the source code

This was an example about spring scopes.

You can download the source code here: springScopingExample.zip
Share.

Comments are closed.