Spring Bean Life Cycle and Callbacks

The Spring Framework provide several call back methods to created a bean and some method to be destroy the bean in the Spring IoC Container.

Spring Bean Life Cycle

@ImageSource-Youtube

The Spring Framework provides several marker 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.

The life cycle of a Spring bean is easy to understand. When a bean is instantiated, it may be required to perform some initialization to get it into a usable state. Similarly, when the bean is no longer required and is removed from the container, some cleanup may be required.

Though, there is lists of the activities that take place behind the scenes between the time of bean Instantiation and its destruction, but this chapter will discuss only two important bean lifecycle callback methods which are required at the time of bean initialization and its destruction.

Beans can be notified after creation and all properties are set, and before they are destroyed and removed from the bean container. This involves specifying the callback method to be invoked by the container. This is done in XML by specifying attributes init-method="myinit", for the initialization callback, and destroy-method="mydestroy", for the destroy callback. "myinit" and "cleanUp" are names of instance methods in the bean class.

Initialization callbacks:
Implementing the org.springframework.beans.factory.InitializingBean interface allows a bean to perform initialization work after all necessary properties on the bean are set by the container. The InitializingBean interface specifies exactly one method:
org.springframework.beans.factory.InitializingBean interface  provide Initialization callbacks method as given below..
void afterPropertiesSet() throws Exception
Now we can implements above interface and do some initialization functionality with in this method. As below..
public class Triangle implements InitializingBean
{
     @Override
     public void afterPropertiesSet() throws Exception
     {
       //To do some initialization works here
       System.out.println("InitializingBean init method is called for Triangle");
     }
}
Generally, the use of the InitializingBean interface can be avoided (and is discouraged since it unnecessarily couples the code to Spring). A bean definition provides support for a generic initialization method to be specified. In the case of XML-based configuration metadata, this is done using the 'init-method' attribute. For example, the following definition:
In the case of XML-based configuration metadata, we can use the init-method attribute to specify the name of the method that has a void no-argument signature. For example:
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Triangle" id="triangle" init-method="myInit"></bean>
Now following has myInit method in class.
public class Triangle
{
  public void myInit()
  {
     //To do some initialization works here
     System.out.println("My init method is called for Triangle");
  }
}
Now using Java annotations can also be used to declare life-cycle callbacks.
public class Triangle
{
  //init callback
  @PostConstruct
  public void myInit()
  {
     //To do some initialization works here
     System.out.println("My init method is called for Triangle");
  }
}


Destruction callbacks:
Implementing the org.springframework.beans.factory.DisposableBean interface allows a bean to get a callback when the container containing it is destroyed. The DisposableBean interface specifies one method:
void destroy() throws Exception
Now we can implements above interface and do some Destruction functionality with in this method. As below..
public class Triangle implements DisposableBean 
{
     @Override
     public void destroy() throws Exception
     {
       //To do some Destruction works here
       System.out.println("DisposableBean destroy method is called for Triangle");
     }
}
Generally, the use of the DisposableBean marker interface can be avoided (and is discouraged since it unnecessarily couples the code to Spring). A bean definition provides support for a generic destroy method to be specified. When using XML-based configuration metadata this is done via the 'destroy-method' attribute on the . For example, the following definition:
In the case of XML-based configuration metadata, we can use the destroy-method attribute to specify the name of the method that has a void no-argument signature. For example:
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Triangle" destroy-method="cleanUp" id="triangle"></bean>
Now following has cleanUp method in class.
public class Triangle
{
  public void cleanUp()
  {
     //To do some Destruction works here
     System.out.println("cleanUp method is called for Triangle");
  }
}
Now using Java annotations can also be used to declare life-cycle callbacks.
public class Triangle
{
  //destroy callback
  @PreDestroy
  public void myInit()
  {
     //To do some Destruction works here
     System.out.println("cleanUp method is called for Triangle");
  }
}
If you are using Spring's IoC container in a non-web application environment; for example, in a rich client desktop environment; you register a shutdown hook with the JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your singleton beans so that all resources are released.

It is recommended that you do not use the InitializingBean or DisposableBean callbacks, because XML configuration gives much flexibility in terms of naming your method.
Lets see the Example:
Triangle.java
package com.dineshonjava.sdnext.callbackLifecycle.tutorial;

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


public class Triangle implements InitializingBean, DisposableBean
{
 private Point pointA;
 private Point pointB;
 private Point pointC;
 /**
  * @param pointA the pointA to set
  */
 public void setPointA(Point pointA) {
  this.pointA = pointA;
 }

 /**
  * @param pointB the pointB to set
  */
 public void setPointB(Point pointB) {
  this.pointB = pointB;
 }

 /**
  * @param pointC the pointC to set
  */
 public void setPointC(Point pointC) {
  this.pointC = pointC;
 }

 public void draw()
 {
System.out.println("PointA is ("+pointA.getX()+", "+pointA.getY()+")");
System.out.println("PointB is ("+pointB.getX()+", "+pointB.getY()+")");
System.out.println("PointC is ("+pointC.getX()+", "+pointC.getY()+")");
 }

 @Override
 public void afterPropertiesSet() throws Exception
 {
System.out.println("InitializingBean init method is called for Triangle");
 }

 @Override
 public void destroy() throws Exception
 {
System.out.println("DisposableBean destroy method is called for Triangle");
 }
 
 public void myInit()
 {
   System.out.println("My init method is called for Triangle");
 }
 
 public void cleanUp()
 {
   System.out.println("cleanUp method is called for Triangle");
 }
}
Point.java
package com.dineshonjava.sdnext.callbackLifecycle.tutorial;

public class Point
{
 private int x;
 private int y;
 /**
  * @return the x
  */
 public int getX() {
  return x;
 }
 /**
  * @param x the x to set
  */
 public void setX(int x) {
  this.x = x;
 }
 /**
  * @return the y
  */
 public int getY() {
  return y;
 }
 /**
  * @param y the y to set
  */
 public void setY(int y) {
  this.y = y;
 }
}
Following is the configuration file Spring.xml required for init and destroy methods.
Spring.xml
<beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans">
  
<bean autowire="byName" class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Triangle" destroy-method="cleanUp" id="triangle" init-method="myInit">
</bean>

<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Point" id="pointA">
  <property name="x" value="0"></property>
  <property name="y" value="0"></property>
</bean>
  
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Point" id="pointB">
   <property name="x" value="-20"></property>
   <property name="y" value="0"></property>
</bean>
  
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Point" id="pointC">
  <property name="x" value="20"></property>
  <property name="y" value="0"></property>
</bean>
</beans>
Once you are done with creating source and bean configuration files, let us run the following application. If everything is fine with your application, this will print the following message:
package com.dineshonjava.sdnext.callbackLifecycle.tutorial;

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


/**
 * @author Dinesh Rajput
 *
 */
public class DrawingApp 
{
 /**
  * @param args
  */
 public static void main(String[] args) 
 {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
context.registerShutdownHook();
Triangle triangle = (Triangle) context.getBean("triangle");
triangle.draw();
 }

}
Output:
Jul 1, 2012 2:48:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@ab50cd: startup date [Sun Jul 01 14:48:33 IST 2012]; root of context hierarchy
Jul 1, 2012 2:48:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Jul 1, 2012 2:48:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1543c88: defining beans [triangle,pointA,pointB,pointC]; root of factory hierarchy
InitializingBean init method is called for Triangle
My init method is called for Triangle
PointA is (0, 0)
PointB is (-20, 0)
PointC is (20, 0)
Jul 1, 2012 2:48:33 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@ab50cd: startup date [Sun Jul 01 14:48:33 IST 2012]; root of context hierarchy
DisposableBean destroy method is called for Triangle
cleanUp method is called for Triangle

Default initialization and destroy methods: If you have too many beans having initialization and or destroy methods with the same name, you don't need to declare init-method and destroy-method on each individual bean. Instead framework provides the flexibility to configure such situation using default-init-method and default-destroy-method attributes on the <beans> element as follows:
spring.xml
<beans default-destroy-method="cleanUp" default-init-method="myInit" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans">
  
<bean autowire="byName" class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Triangle" id="triangle">
</bean>
  
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Point" id="pointA">
  <property name="x" value="0"></property>
  <property name="y" value="0"></property>
</bean>
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Point" id="pointB">
  <property name="x" value="-20"></property>
  <property name="y" value="0"></property>
</bean>
<bean class="com.dineshonjava.sdnext.callbackLifecycle.tutorial.Point" id="pointC">
  <property name="x" value="20"></property>
  <property name="y" value="0"></property>
</bean>
</beans>
Now again run the application with above the configuration file we will get the following output:
Output:
Jul 1, 2012 2:58:00 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@ab50cd: startup date [Sun Jul 01 14:58:00 IST 2012]; root of context hierarchy
Jul 1, 2012 2:58:00 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Jul 1, 2012 2:58:01 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1543c88: defining beans [triangle,pointA,pointB,pointC]; root of factory hierarchy
InitializingBean init method is called for Triangle
My init method is called for Triangle
PointA is (0, 0)
PointB is (-20, 0)
PointC is (20, 0)
DisposableBean destroy method is called for Triangle
cleanUp method is called for Triangle


In Next Chapter we will discuss about to Writing BeanPostProcessor in Spring.
                                          
                                                        << Previous || Next >> 



3 comments:

  1. Very helpful tutorial..

    ReplyDelete
  2. not only good stuff but also you made blogspot look like your own website.

    ReplyDelete
  3. not only good stuff but also you made blogspot look like your own website.

    ReplyDelete