Core Spring : Bean Scopes, Lifecycle

When we start the spring container, i.e when we define ApplicationContext with spring.xml file, the spring container creates all the bean at that time itself (before we call any bean). Bean scope is used to decide which type of bean instance should be return from Spring container back to the caller. The Spring Framework supports exactly five scopes (of which three are available only if you are using a web-aware ApplicationContext).

Scope Description
singleton Scopes a single bean definition to a single object instance per Spring IoC container.
prototype Return a new bean instance each time when requested
request Scopes a single bean definition to the lifecycle of a single HTTP request; that is each and every HTTP request will have its own instance of a bean created off the back of a single bean definition.
session Scopes a single bean definition to the lifecycle of a HTTP Session.
global session Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context.

The singleton scope

Singleton is the default scope. when you define a bean definition and it is scoped as a singleton, then the Spring IoC container will create exactly one instance of the object defined by that bean definition. This single instance will be stored in a cache of such singleton beans, and all subsequent requests and references for that named bean will result in the cached object being returned. 

Employee.java :


package mynotes.corespring.test;

public class Employee {

private int id;
private String name;

public Employee(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return ("Id=>" + this.id + " Name=>" + this.name);
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

spring.xml:


<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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

&nbsp;

<bean id="employee" class="mynotes.corespring.test.Employee">
<constructor-arg value="John" type="String"></constructor-arg>
<constructor-arg value="20" type="int"></constructor-arg>
</bean>
</beans>

Modifying our App.java:

package mynotes.corespring.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

 public static void main(String[] args) {
 ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
 Employee employee=(Employee) context.getBean("employee");
 System.out.println(employee);
 employee.setId(21);
 Employee employee_new_obj=(Employee) context.getBean("employee");
 System.out.println(employee_new_obj);

 }

}

We have not changed the spring.xml, so the default bean created will be of ‘singleton’ scope. Output of the programm:

Id=>20 Name=>John
Id=>21 Name=>John

Notice that we changed the id in employee obj and when again called the object using spring , we got back the same object as made previously. Please be aware that Spring’s concept of a singleton bean is quite different from the Singleton pattern as defined in the seminal Gang of Four (GoF) patterns book. The GoF Singleton hard codes the scope of an object such that one and only one instance of a particular class will ever be created per ClassLoader. The scope of the Spring singleton is best described as per container and per bean.

The prototype scope

The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made (that is, it is injected into another bean or it is requested via a programmatic getBean() method call on the container). As a rule of thumb, you should use the prototype scope for all beans that are stateful, while the singleton scope should be used for stateless beans. Using scope=”prototype” in spring.xml for out employee bean. Output will be :

Id=>20 Name=>John
Id=>20 Name=>John

When using singleton-scoped beans that have dependencies on beans that are scoped as prototypes, please be aware that dependencies are resolved at instantiation time. This means that if you dependency inject a prototype-scoped bean into a singleton-scoped bean, a brand new prototype bean will be instantiated and then dependency injected into the singleton bean. That exact same prototype instance will be the sole instance that is ever supplied to the singleton-scoped bean. As of Spring 2.0, the bean scoping mechanism in Spring is extensible. This means that you are not limited to just the bean scopes that Spring provides out of the box; you can define your own scopes.

Till now we have written a code that initialzes the ApplicationContext (ApplicationContext context=new ClassPathXmlApplicationContext(“spring.xml”);) , we havent closed the ApplicationContext. To do this we need to register the shutdown hook for your java program so that when your main method ends, the hook is called and the context shutdown. This is only required for SE application, if your using web-application or other spring knows when to call shutdown. To register the shutdown hook for desktop application we have to use another class – ‘AbstractApplicationContext‘ , to register just call context.registerShutdownHook();.

The Spring Framework provides several callback interfaces to change the behavior of your bean in the container; they include InitializingBean and DisposableBean. Implementing these interfaces will result in the container calling afterPropertiesSet() for the former and destroy() for the latter to allow the bean to perform certain actions upon initialization and destruction. Employee.java:


package mynotes.corespring.test;

import org.springframework.beans.factory.DisposableBean;

import org.springframework.beans.factory.InitializingBean;

public class Employee implements InitializingBean,DisposableBean{

//Other code same as previous

public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet called after bean initialized");
}

public void destroy() throws Exception {
System.out.println("destroy called before bean get destroyed");

}

App.java:


package mynotes.corespring.test;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
context.registerShutdownHook();
Employee employee=(Employee) context.getBean("employee");
System.out.println(employee);
}

}

Output:


afterPropertiesSet called after bean initialized
Id=>20 Name=>John
destroy called before bean get destroyed

Spring does not manage the complete lifecycle of a prototype bean (so if your bean scope is prototype – destroy method wont be called): the container instantiates, configures, decorates and otherwise assembles a prototype object, hands it to the client and then has no further knowledge of that prototype instance. This means that while initialization lifecycle callback methods will be called on all objects regardless of scope, in the case of prototypes, any configured destruction lifecycle callbacks will not be called. It is the responsibility of the client code to clean up prototype scoped objects and release any expensive resources that the prototype bean(s) are holding onto. (One possible way to get the Spring container to release resources used by prototype-scoped beans is through the use of a custom bean post-processor which would hold a reference to the beans that need to be cleaned up.)

One of the disadvantage of above approach is that you have to bnd you Employee class with Spring. This can be done by tellig spring.xml (init-method destroy-method attributes) which methds to run for inIt and destroy. Lets myCustomInit() and myCustomDestroy() be the methods we want to run. Removing Spring stuffs from Employee.java and ading these:


package mynotes.corespring.test;

public class Employee {

//Other code same as previous with no spring stuff

public void myCustomInit(){
System.out.println("myCustomInit called after bean initialized");
}

public void myCustomDestroy(){
System.out.println("myCustomDestroy called before bean get destroyed");
}
}

spring.xml:


<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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

&nbsp;

<bean id="employee" class="mynotes.corespring.test.Employee"
init-method="myCustomInit" destroy-method="myCustomDestroy">
<constructor-arg value="John" type="String"></constructor-arg>
<constructor-arg value="20" type="int"></constructor-arg>
</bean>
</beans>

Running App.java


myCustomInit called after bean initialized
Id=>20 Name=>John
myCustomDestroy called before bean get destroyed

Suppose these name myCustomInit and myCustomDestroy are same for all your beans, so instead of writing again and again in all your beans , you can write in the <beans> tag using default-init-method default-destroy-method. If these methods are found spring will run it or else ignore them. spring.xml:


<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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd"
default-init-method="myCustomInit"
default-destroy-method="myCustomDestroy">

&nbsp;

<bean id="employee" class="mynotes.corespring.test.Employee" >
<constructor-arg value="John" type="String"></constructor-arg>
<constructor-arg value="20" type="int"></constructor-arg>
</bean>
</beans>

What if we used both at the same time. Employee.java:


package mynotes.corespring.test;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class Employee implements InitializingBean, DisposableBean {

private int id;
private String name;

public Employee(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return ("Id=>" + this.id + " Name=>" + this.name);
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet called after bean initialized");
}

public void destroy() throws Exception {
System.out.println("destroy called before bean get destroyed");

}

public void myCustomInit() {
System.out.println("myCustomInit called after bean initialized");
}

public void myCustomDestroy() {
System.out.println("myCustomDestroy called before bean get destroyed");
}

}

Running App.java will give following output:


afterPropertiesSet called after bean initialized
myCustomInit called after bean initialized
Id=>20 Name=>John
destroy called before bean get destroyed
myCustomDestroy called before bean get destroyed

There is another way for configuring init and destroy using annotation (PostConstruct and PreDestroy) which we will see afterwards.

Another point to note that is when using ApplicationContext, the singleton scope bean gets created when the configuration files loads, while the prototype beans gets created only when getBean() method is called. For BeanFactory, beans gets created only when getBean() is called and DI occurs accordingly.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: