Spring Bean Lifecycle

In this article, we will discuss about the Spring bean’s lifecycle mechanism or steps involved from bean creation using Reflection API till its destruction in Spring IOC container. The container creates, manages & dispenses the beans and doesn’t provide any out-of –box features to interact with the beans other than lifecycle callback interfaces and methods. So, one has to understand these lifecycle callbacks/methods to leverage the full use of Spring features in an enterprise application.

Steps involved in the lifecycle of Spring bean

  • Instantiate – container creates an instance using Reflection API
  • Populate properties – simple properties & collaborating beans are resolved and set
  • setBeanName() – sets the bean name in the bean factory passing bean’s Id, if it implements BeanNameAware interface
  • setBeanFactory() – passes the beanFactory to the bean instance, if it implements BeanFactoryAware interface
  • Pre-Initialization – if there are any BeanPostProcessor associated with the BeanFactory, then postProcessBeforeInitialization() method will be invoked (even before properties are set)
  • Initialization –
    1. If the bean implement InitializingBean interface then its overrided method afterPropertiesSet() will be invoked
    2. If we got init-method attribute in the bean definition, then its value will be resolved to a method name in the Bean class
    3. With JSR-250, @PostConstruct is considered the best approach to put up initialization block in place of above two approach (Since Spring 2.5)
    4. NOTE: will see sequence for initialization, what if we got all three in the Bean class
  • Post-Initialization – Similar to Pre-Initialization step if there are any BeanPostProcessor associated with the BeanFactory, then postProcessAfterInitialization() method will be invoked
  • Ready to serve/use – in usable state
  • Destruction/Destroy –
    1. If the bean implement DisposableBean interface then its overrided method destroy () will be invoked
    2. If we got destroy-method attribute in the bean definition, then its value will be resolved to a method name in the Bean class
    3. With JSR-250, @PreDestroy is considered the best approach to put up destruction/cleaning block in place of above two approach (Since Spring 2.5)
    4. NOTE: We will see sequence for destroy, what if we got all three in the Bean class

The Sequence!!

Since Spring 2.5, we got three options for controlling the lifecycle behavior

  1. Implementing InitializingBean & DisposableBean callback interface
  2. Custom init() & destroy() methods; referred using bean attribute init-method and destroy-method
  3. With JSR-250 specification, @PostContruct & @PreDestroy annotations can be used.

Now, let’s list down the sequence for Initialization & Destroy separately

Initialization

  1. Method annotated with @PostConstruct
  2. afterPropertiesSet() as defined by the InitializingBean callback interface
  3. A custom configured init() method

Destroy

  1. Methods annotated with @PreDestroy
  2. destroy() as defined by the DisposableBean callback interface
  3. A custom configured destroy() method

BeanPostProcessor

BeanPostPrecessor interface are responsible for the pre-initialization and post-initialization steps in the lifecycle of a Spring Bean. If you read through the API doc for BeanPostProcessor, it has two methods namely postProcessBeforeInitialization() & postProcessAfterInitialization() which are invoked if they are associated with the Spring IOC container’s BeanFactory.

These are basically to help you out to customize your logic overriding the default behavior of the IOC container like instantiation logic, dependency-resolution logic, and so forth. Generally, spring takes cares of the bean instantiation when required and manages its lifecycle and dispenses it upon completion of its work. If the requirement is to have more than one BeanPostProcessor, then developer should consider implementing ordered interface.

This BeanPostProcessor beans defined in the spring configuration xml are auto-detected and it’s applied to all the beans before they are created.

For details about BeanPostProcessor, check Spring doc and API doc

postProcessBeforeInitialization

Object postProcessBeforeInitialization(Object bean,
                                       String beanName)
                                throws BeansException

postProcessAfterInitialization

Object postProcessAfterInitialization(Object bean,
                                      String beanName)
                               throws BeansException

Let’s see a detailed example demonstrating all the above lifecycle callback methods

Technology Used

  • Java 1.7
  • Eclipse Kepler IDE
  • Maven 3.0.4
  • Spring-4.0.0-RELEASE

Mavenize or download required jars

Add Spring-4.0.0 dependencies to the pom.xml

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
	<version>${spring.version}</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>${spring.version}</version>
</dependency>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the spring site and include them in the classpath

  • spring-core-4.0.0-RELEASE
  • spring-context-4.0.0-RELEASE
  • spring-beans-4.0.0-RELEASE
  • spring-aop-4.0.0-RELEASE
  • spring-expression-4.0.0-RELEASE
  • commons-logging-1.1.1
  • aopalliance-1.0

Let’s see coding in action

 

Create Employee Class with lifecycle callback methods

Simple bean class with two properties viz., name & age and its setter/getter for setter dependency injection

In addition to this, there are few callback interfaces are implemented by this bean for lifecycle mechanism understanding as explained in the above step under the heading <Steps involved in the lifecycle of Spring bean>

Employee.java

package com.spring.series.bean.lifecycle;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Employee implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

	// instance variables
	private String name;
	private int age;

	/**
	 * getter and setter
	 */
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public void setBeanName(String bean) {
		System.out.println("\nBeanNameAware/setBeanName : " + bean);
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		System.out.println("\nBeanFactoryAware/setBeanFactory : " + beanFactory);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println("\nApplicationContextAware/setApplicationContext : " + applicationContext);
	}

	@PostConstruct
	public void customInit(){
		System.out.println("Methods annotated with @PostConstruct");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("\nafterPropertiesSet() as defined by the InitializingBean callback interface");
	}

	// declared in bean definition with attribute init-method="initXML"
	public void initXML(){
		System.out.println("\nA custom configured init() method : setting attribute init-method attribute in the bean defintion");
	}

	/**
	 * bean method to printout the details of the employee instance
	 */
	public void printEmployeeDetails() {

		System.out.println("******************************************************************");
		System.out.println("Employee Name \t\t: " + name);
		System.out.println("Employee Age \t\t: " + age);
		System.out.println("******************************************************************");
	}

	@PreDestroy
	public void customDestroy(){
		System.out.println("\nMethods annotated with @PreDestroy");
	}

	@Override
	public void destroy() throws Exception {
		System.out.println("\ndestroy() as defined by the DisposableBean callback interface");
	}

	// declared in bean definition with attribute destroy-method="destroyXML"
	public void destroyXML(){
		System.out.println("\nA custom configured destroy() method : setting attribute destroy-method attribute in the bean defintion");
	}
}

Create a Bean implementing BeanPostProcessor for custom modification of the beans

Simple bean implementing BeanPostProcessor interface and overriding its methods postProcessBeforeInitialization() & postProcessAfterInitialization() for interacting with IOC container for custom modification

package com.spring.series.bean.lifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class SpringBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("\n**********BeanPostProcessor postProcessBeforeInitialization**********");
		System.out.println("Bean \"" + beanName + "\" created before lifecylce mechanism : " + bean.toString());
		System.out.println("*********************************************************************\n");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("\n**********BeanPostProcessor postProcessAfterInitialization**********");
		System.out.println("Bean \"" + beanName + "\" created : " + bean.toString());
		System.out.println("*********************************************************************\n");
		return bean;
	}
}

Create Spring Bean Configuration file (Spring XML)

Bean definition for Employee class and another bean which implements BeanPostProcessor interface

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

	<!-- to turn on annotation-based configuration -->
	<context:annotation-config />

	<!-- bean definition for BeanPostProcessor -->
	<bean id="springBeanPP"
		class="com.spring.series.bean.lifecycle.SpringBeanPostProcessor" />

	<!-- a simple bean definition for Employee class with its properties setter
		injected -->
	<bean id="employee" class="com.spring.series.bean.lifecycle.Employee"
		init-method="initXML" destroy-method="destroyXML">
		<property name="name" value="Mark" />
		<property name="age" value="32" />
	</bean>

</beans>

Note: Name of the Spring Bean Configuration file can be anything (not necessary to have SpringContext.xml) and it’s your choice. But, in the enterprise application keep these file names appropriate to the business context. So that it will increase the readability of the application.

Project Structure in Eclipse (Package Explorer view)

SpringBeanLifeCycle

 

Test the Application that’s exactly …. Run it!

Let’s test using AbstractApplicationContext

Loads the context xml from the classpath and invokes the variant method getBean() to get the instance of the Employee class.

Next step, as usual invokes the bean’s business method. But upon invoking there are few lifecycle callback methods are invoked implicitly as we have implemented those interfaces.

Have a look at the output, carefully!! To check how it goes!!

TestSpringBeanLifecycle.java

package com.spring.series.bean.lifecycle;

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

public class TestSpringBeanLifecycle {

	public static void main(String[] args) {
		testSpringBeanLifecycle();
	}

	private static void testSpringBeanLifecycle(){

		// loading spring context xml from classpath
		AbstractApplicationContext abstractApplicationContext = new ClassPathXmlApplicationContext("com/spring/series/bean/lifecycle/SpringContext.xml");

		// getting bean from XML & cast it
		Employee employee = (Employee) abstractApplicationContext.getBean("employee");

		// invoking business method of Employee bean
		employee.printEmployeeDetails();

		// registering to shutdown the bean - destroying explicitly the bean from Spring IOC container
		abstractApplicationContext.registerShutdownHook();
	}
}

Output in console

Aug 03, 2014 4:47:16 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@34c7da23: startup date [Sun Aug 03 16:47:16 IST 2014]; root of context hierarchy
Aug 03, 2014 4:47:16 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [com/spring/series/bean/lifecycle/SpringContext.xml]

BeanNameAware/setBeanName : employee

BeanFactoryAware/setBeanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@ae0d42a: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,springBeanPP,employee,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor]; root of factory hierarchy

ApplicationContextAware/setApplicationContext : org.springframework.context.support.ClassPathXmlApplicationContext@34c7da23: startup date [Sun Aug 03 16:47:16 IST 2014]; root of context hierarchy

**********BeanPostProcessor postProcessBeforeInitialization**********
Bean "employee" created before lifecylce mechanism : com.spring.series.bean.lifecycle.Employee@4180874
*********************************************************************

Methods annotated with @PostConstruct

afterPropertiesSet() as defined by the InitializingBean callback interface

A custom configured init() method : setting attribute init-method attribute in the bean defintion

**********BeanPostProcessor postProcessAfterInitialization**********
Bean "employee" created : com.spring.series.bean.lifecycle.Employee@4180874
*********************************************************************

******************************************************************
Employee Name 		: Mark
Employee Age 		: 32
******************************************************************
Aug 03, 2014 4:47:17 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@34c7da23: startup date [Sun Aug 03 16:47:16 IST 2014]; root of context hierarchy

Methods annotated with @PreDestroy

destroy() as defined by the DisposableBean callback interface

A custom configured destroy() method : setting attribute destroy-method attribute in the bean defintion

Download project

Spring Bean Lifecycle (3kB)

Happy Coding !!
Happy Learning !!