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 –
- If the bean implement InitializingBean interface then its overrided method afterPropertiesSet() will be invoked
- If we got init-method attribute in the bean definition, then its value will be resolved to a method name in the Bean class
- With JSR-250, @PostConstruct is considered the best approach to put up initialization block in place of above two approach (Since Spring 2.5)
- 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 –
- If the bean implement DisposableBean interface then its overrided method destroy () will be invoked
- If we got destroy-method attribute in the bean definition, then its value will be resolved to a method name in the Bean class
- 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)
- 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
- Implementing InitializingBean & DisposableBean callback interface
- Custom init() & destroy() methods; referred using bean attribute init-method and destroy-method
- With JSR-250 specification, @PostContruct & @PreDestroy annotations can be used.
Now, let’s list down the sequence for Initialization & Destroy separately
Initialization
- Method annotated with @PostConstruct
- afterPropertiesSet() as defined by the InitializingBean callback interface
- A custom configured init() method
Destroy
- Methods annotated with @PreDestroy
- destroy() as defined by the DisposableBean callback interface
- 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)
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 !!