Spring Bean Life Cycle Management Example


In this tutorial, we will discuss how to interact with the container’s management of beans life cycle or what is the life cycle of a bean and how to manage it. In the spring bean lifecycle, initialization and destruction callbacks are involved. You can manage it and there are three possible ways to manage the lifecycle of beans. Every bean has life cycle i.e. bean started, bean running and finally bean destroyed.

Three ways to manage the lifecycle of beans in Spring framework.

  1. Programmatic approach
  2. XML-based approach
  3. Annotation approach

Technologies Used

Check the list of all technology which is used in our example.

  1. Spring-5.0.2.REALESE JARs
  2. JDK 8
  3. Eclipse Mars IDE
  4. Oracle 11g

Block Diagram of Spring Beans Life Cycle

This diagram will help you to understand how a bean traverse.

Spring Bean Life Cycle Management Example

Table Structure

Find the table structure of EMPLOYE_DETAILS used in our example.

employee_details.sql
CREATE TABLE "EMPLOYEE_DETAILS" (
    "NAME"     VARCHAR2(30 BYTE),
    "EMAIL"    VARCHAR2(100 BYTE),
    "DEPT"     VARCHAR2(20 BYTE),
    "SALARY"   NUMBER(10,0)
);

In this example, I have used the database connection and treated it as a bean. To interact with the database and inserting some records first we create the connection object, then perform the insertion and finally close the connection.

Let’s see the complete example of all possible ways to manage the lifecycle of bean one by one.

1 Programmatic Approach

In this approach, the bean class implement the Spring InitializingBean and DisposableBean interfaces. These interfaces has the only one method afterPropertiesSet() and destroy() respectively.

The InitializingBean interface allows a bean to perform initialization work after all necessary properties on the bean have been set by the container and DisposableBean interface allows a bean to get a callback when the container containing it is destroyed.

1.1 Spring Beans

Create the Employee bean class, implement the InitializingBean and DisposableBean interfaces and override its methods.

public void afterPropertiesSet()– At the time of loading container will call this method. Use the inject values to create the Connection object.

public void save()– Now Connection object is ready. Use it to insert the employee records into the table.

public void destroy()– Close the Connection object when the task has been completed.

Employee.java
package org.websparrow.programmatic.beans;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

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

public class Employee implements InitializingBean, DisposableBean {

	// Generate setters method...
	private String driver, url, user, password;

	private Connection con;

	// Initialization operation
	@Override
	public void afterPropertiesSet() throws Exception {
		Class.forName(driver);
		con = DriverManager.getConnection(url, user, password);
		System.out.println("Debug--> database connection opened...");
	}

	// Task perform operation
	public void save(String name, String email, String dept, int salary) throws Exception {

		PreparedStatement ps = con.prepareStatement("INSERT INTO EMPLOYEE_DETAILS VALUES(?,?,?,?)");
		ps.setString(1, name);
		ps.setString(2, email);
		ps.setString(3, dept);
		ps.setInt(4, salary);
		ps.executeUpdate();

		System.out.println("Debug--> emplopyee records inserted...");
	}

	// Clean up operation
	@Override
	public void destroy() throws Exception {
		con.close();
		System.out.println("Debug--> database connection closed...");
	}
}

1.2 Spring Beans Configuration

In the configuration file, inject the value to create the Connection object like driver name, url, user name and password.

spring-programmatic.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
 "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>

	<bean id="emp" class="org.websparrow.programmatic.beans.Employee">
		<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="user" value="root" />
		<property name="password" value="root" />
	</bean>

</beans>

1.3 Run it

Load the configuration file using ConfigurableApplicationContext interface and run it.

goes here.

ProgrammaticClient.java
package org.websparrow.test;

import java.util.Scanner;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.websparrow.programmatic.beans.Employee;

public class ProgrammaticClient {
	public static void main(String[] args) throws Exception {

		ConfigurableApplicationContext cap = new ClassPathXmlApplicationContext("spring-programmatic.xml");

		while (true) {
			System.out.println("Press 1 to SAVE the records and 2 for DESTROY");
			Scanner sc = new Scanner(System.in);
			int i = sc.nextInt();
			switch (i) {
			case 1:
				Employee emp = (Employee) cap.getBean("emp");
				emp.save("Diksha", "[email protected]", "Sales", 196261);
				break;

			default:
				cap.close();
				break;
			}
		}
	}
}

1.4 Output

Run the client class and you have seen at the time of loading container will create the connection object. Press 1 to save the employee record into the database and the press the 2 to close the connection object.

Mar 17, 2018 8:59:49 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-programmatic.xml]
Debug--> database connection opened...
Press 1 to SAVE the records and 2 for DESTROY
1
Debug--> emplopyee records inserted...
Press 1 to SAVE the records and 2 for DESTROY
2
Mar 17, 2018 9:00:15 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org[email protected]4bf558aa: startup date [Sat Mar 17 20:59:49 IST 2018]; root of context hierarchy
Debug--> database connection closed...

Note: Spring will not recommended to use the InitializingBean and DisposableBean interfaces because it makes you Spring interface dependent.

2 XML-based Approach

In the XML-based approach, we don’t need to implement the InitializingBean and DisposableBean interfaces. Instead of these interface, we can use the init-method attribute to specify the name of the method that has a void no-argument signature for initialization and destroy-method attribute for clean-up of the <bean/> tag.

2.1 Spring Beans

I have used the above Employee bean class here but did not implement the spring interface. Here I have created three method to manage the life cycle of bean.

public void conInit()– This is a init method. It will call by the container at the time of loading. This method will be passed in the init-method attribute.

public void save()– Use it to insert the employee records into the table when connection abject is ready to use.

public void conCleanUp()– It similar to destroy() method. Pass the method name in the destroy-method attribute.

Employee.java
package org.websparrow.xml.beans;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

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

public class Employee {

	// Generate setters method...
	private String driver, url, user, password;

	private Connection con;

	// Initialization operation
	public void conInit() throws Exception {
		Class.forName(driver);
		con = DriverManager.getConnection(url, user, password);
		System.out.println("Debug--> database connection opened...");
	}

	// Task perform operation
	public void save(String name, String email, String dept, int salary) throws Exception {
		PreparedStatement ps = con.prepareStatement("INSERT INTO EMPLOYEE_DETAILS VALUES(?,?,?,?)");
		ps.setString(1, name);
		ps.setString(2, email);
		ps.setString(3, dept);
		ps.setInt(4, salary);
		ps.executeUpdate();
		System.out.println("Debug--> emplopyee records inserted...");
	}

	// Clean up operation
	public void conCleanUp() throws Exception {
		con.close();
		System.out.println("Debug--> database connection closed...");
	}
}

2.2 Spring Beans Configuration

In configuration file pass the name of the initializing method to the init-method attribute and destroy method name to the destroy-method attribute.

spring-xml.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
 "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>

	<bean id="emp" class="org.websparrow.xml.beans.Employee" init-method="conInit" destroy-method="conCleanUp">
		<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="user" value="root" />
		<property name="password" value="root" />
	</bean>

</beans>

2.3 Run it

Load the configuration file and run it.

XmlClient.java
package org.websparrow.test;

import java.util.Scanner;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.websparrow.xml.beans.Employee;

public class XmlClient {
	public static void main(String[] args) throws Exception {

		ConfigurableApplicationContext cap = new ClassPathXmlApplicationContext("spring-xml.xml");

		while (true) {
			System.out.println("Press 1 to SAVE the records and 2 for DESTROY");
			Scanner sc = new Scanner(System.in);
			int i = sc.nextInt();
			switch (i) {
			case 1:
				Employee emp = (Employee) cap.getBean("emp");
				emp.save("Divya", "[email protected]", "IT", 19626);
				break;

			default:
				cap.close();
				break;
			}
		}
	}
}

2.4 Output

You will get the desired result based on your input.

Mar 17, 2018 9:06:44 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-xml.xml]
Debug--> database connection opened...
Press 1 to SAVE the records and 2 for DESTROY
1
Debug--> emplopyee records inserted...
Press 1 to SAVE the records and 2 for DESTROY
2
Mar 17, 2018 9:06:52 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org[email protected]4bf558aa: startup date [Sat Mar 17 21:06:44 IST 2018]; root of context hierarchy
Debug--> database connection closed...

2.5 Default initialization and destroy methods

When you write initialization and destroy method callbacks that do not use the Spring-specific InitializingBean and DisposableBean callback interfaces, you typically write methods with names such as myInit(), myService, myDestroy(), and so on. Ideally, the names of such lifecycle callback methods are standardized across a project so that all developers use the same method names and ensure consistency.

The presence of the default-init-method attribute on the top-level <beans/> element attribute causes the Spring IoC container to recognize a method called myInit on beans as the initialization method callback. When a bean is created and assembled, if the bean class has such a method, it is invoked at the appropriate time. You configure destroy method callbacks similarly (in XML, that is) by using the default-destroy-method attribute on the top-level <beans/> element. Where existing bean classes already have callback methods that are named at variance with the convention, you can override the default by specifying (in XML, that is) the method name using the init-method and destroy-method attributes of the <beans/> itself.

Suppose that your initialization callback methods are named myInit(), and destroy callback methods are named myDestroy() then the following configuration file looks like…

<beans default-init-method="myInit" default-destroy-method="myDestroy">

	<bean id="emp" class="org.websparrow.xml.beans.Employee">
		<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="user" value="root" />
		<property name="password" value="root" />
	</bean>

</beans>

3 Annotation Approach

Spring bean life cycle can also be managed by using annotation. Spring provide two annotations @PostConstruct for initialization and @PreDestroy for clean-up. Put @PostConstruct annotation at the top of the method where you want to perform the initialization task and @PreDestroy where you want to perform destruction work. To activate these annotation you must need to initialize the CommonAnnotationBeanPostProcessor class.

3.1 Spring Beans

I have just copy the Employee class from the XML-based approach and put the @PostConstruct annotation at the top of the conInit() method to make it as init method and @PreDestroy at the top of the conCleanUp() method to make it as destroy method.

Employee.java
package org.websparrow.annotation.beans;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

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

import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;

public class Employee {

	// Generate setters method...
	private String driver, url, user, password;

	private Connection con;

	@PostConstruct
	public void conInit() throws Exception {
		Class.forName(driver);
		con = DriverManager.getConnection(url, user, password);
		System.out.println("Debug--> database connection opened...");
	}

	public void save(String name, String email, String dept, int salary) throws Exception {
		PreparedStatement ps = con.prepareStatement("INSERT INTO EMPLOYEE_DETAILS VALUES(?,?,?,?)");
		ps.setString(1, name);
		ps.setString(2, email);
		ps.setString(3, dept);
		ps.setInt(4, salary);
		ps.executeUpdate();
		System.out.println("Debug--> emplopyee records inserted...");
	}

	@PreDestroy
	public void conCleanUp() throws Exception {
		con.close();
		System.out.println("Debug--> database connection closed...");
	}

}

3.2 Spring Beans Configuration

Inside the configuration file activate the annotation by initializing CommonAnnotationBeanPostProcessor class and do the setter DI for Employee bean.

spring-annoation.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
 "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>

	<!-- activate annotation -->
	<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

	<bean id="emp" class="org.websparrow.annotation.beans.Employee">
		<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="user" value="root" />
		<property name="password" value="root" />
	</bean>

</beans>

3.3 Run it

Load the configuration file and run it.

AnnotationClient.java
package org.websparrow.test;

import java.util.Scanner;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.websparrow.annotation.beans.Employee;

public class AnnotationClient {
	public static void main(String[] args) throws Exception {

		ConfigurableApplicationContext cap = new ClassPathXmlApplicationContext("spring-annotation.xml");

		while (true) {
			System.out.println("Press 1 to SAVE the records and 2 for DESTROY");
			Scanner sc = new Scanner(System.in);
			int i = sc.nextInt();
			switch (i) {
			case 1:
				Employee emp = (Employee) cap.getBean("emp");
				emp.save("Kumkum", "[email protected]", "Sales", 19626);
				break;

			default:
				cap.close();
				break;
			}
		}
	}
}

3.4 Output

You will get the following result on your console log. Do check the database table also.

Mar 17, 2018 9:21:02 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-annotation.xml]
Debug--> database connection opened...
Press 1 to SAVE the records and 2 for DESTROY
1
Debug--> emplopyee records inserted...
Press 1 to SAVE the records and 2 for DESTROY
2
Mar 17, 2018 9:21:10 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org[email protected]4bf558aa: startup date [Sat Mar 17 21:21:02 IST 2018]; root of context hierarchy
Debug--> database connection closed...

How does it work?

Whenever you execute the code, Spring container will read the XML configuration file and create the Employee class object and inject the property via the setter method. After injecting the property Spring container will execute the initialization method. All this will happens at the loading time. And whenever you try to call close() method of ConfigurableApplicationContext container, it will call the destruction method.


About the Author

Atul Rai
I like sharing my experiments and ideas with everyone by writing articles on the latest technological trends.