Spring Bean Lifecycle

Spring Bean Lifecycle

0 Comments

The Spring IoC (Inversion of Control) container manages Spring beans. A “Spring bean” is just a Spring managed instantiation of a Java class.

The Spring IoC container is responsible for instantiating, initializing, and wiring beans. The container also manages the life cycle of beans.

Spring provides several ways through which you can tap into the bean lifecycle. For example, once a bean is instantiated, you might need to perform some initialization to get the bean into a usable state. Similarly, you might need to clean up resources before a bean is removed from the container.

In this post, we will examine the steps of Spring bean lifecycle. This is how the Spring Framework creates and destroys Spring beans.

Spring Bean Lifecycle Overview

This Figure shows two parts of the Spring bean lifecycle:

Part 1: Shows the different stages a bean goes through after instantiation until it is ready for use.
Part 2: Shows what happens to a bean once the Spring IoC container shuts down.

As you can see in Part 1 of the preceding figure, the container instantiates a bean by calling its constructor and then populates its properties.

This is followed by several calls to the bean until the bean is in the ready state.

Similarly, as shown in Part 2, when the container shuts down, the container calls the bean to enable it to perform any required tasks before the bean is destroyed.

Aware interfaces

Spring provides several aware interfaces. These are used to access the Spring Framework infrastructure. The aware interfaces are largely used within the framework and rarely used by Spring programmers.

You as Spring programmers should be familiar with the following three aware interfaces.

  • BeanFactoryAware: Provides setBeanFactory(), a callback that supplies the owning factory to the bean instance.
  • BeanNameAware: ThesetBeanName() callback of this interface supplies the name of the bean.
  • ApplicationContextAware: ThesetApplicationContext() method of this interface provides the ApplicationContext object of this bean.

The code to use the preceding aware interfaces is this.

package guru.springframework.springbeanlifecycle.awareinterfaces.domain;

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.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Arrays;

public class AwareBeanImpl implements ApplicationContextAware, BeanNameAware, BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory method of AwareBeanImpl is called");
System.out.println("setBeanFactory:: AwareBeanImpl singleton= "
+ beanFactory.isSingleton("awareBean"));
}

@Override
public void setBeanName(String beanName) {
System.out.println("setBeanName method of AwareBeanImpl is called");
System.out.println("setBeanName:: Bean Name defined in context= "
+ beanName);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("setApplicationContext method of AwareBeanImpl is called");
System.out.println("setApplicationContext:: Bean Definition Names= "
+ Arrays.toString(applicationContext.getBeanDefinitionNames()));
}
}

The preceding bean implements the ApplicationContextAware, BeanNameAware and BeanFactoryAware interfaces. In the preceding code:

Line 13 – Line 18: The code overrides the setBeanFactory() method of the BeanFactoryAware interface. During runtime, Spring passes the BeanFactory object that created the bean. The code uses the BeanFactory object to print whether or not this bean is a singleton.
Line 20 – Line 25 overrides the setBeanName() method of the BeanNameAware interface. During runtime, Spring passes the name of the bean as a String that the code prints out. The code uses the beanName to print the bean name defined in context.
In Line 27 – Line 32, the code overrides the setApplicationContext() method of the ApplicationContextAware interface. During runtime, Spring passes the ApplicationContext object that created the bean. The code uses the ApplicationContext object to print the bean definition names.

Next, we will write the bean configuration to define the AwareBeanImpl.
The code of the beans.xml is this.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!--    awareinterfaces-->
   <bean id="awareBean" class="guru.springframework.springbeanlifecycle.awareinterfaces.domain.AwareBeanImpl">
   </bean>

</beans>

Finally, let us write the main class which will load the beans.xml and test the aware interface methods.

package guru.springframework.springbeanlifecycle;

import guru.springframework.springbeanlifecycle.awareinterfaces.domain.AwareBeanImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SpringBeanLifecycleApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBeanLifecycleApplication.class, args);

// -------awareinterfaces---------
ApplicationContext context1 =
new ClassPathXmlApplicationContext("beans.xml");
AwareBeanImpl awareBeanImpl = (AwareBeanImpl) context1.getBean("awareBean");
((AbstractApplicationContext) context1).registerShutdownHook();

}

}

The output on running the main class is this:

 
setBeanName method of AwareBeanImpl is called
setBeanName:: Bean Name defined in context= awareBean
setBeanFactory method of AwareBeanImpl is called
setBeanFactory:: AwareBeanImpl singleton= true
setApplicationContext method of AwareBeanImpl is called
setApplicationContext:: Bean Definition Names= [awareBean]

Bean Post Processor

Spring provides the BeanPostProcessor interface that gives you the means to tap into the Spring context life cycle and interact with beans as they are processed.

The BeanPostProcessor interface contains two methods.

  • postProcessBeforeInitialization: Spring calls this method after calling the methods of the aware interfaces and before any bean initialization callbacks, such as InitializingBean’s afterPropertiesSet or a custom init-method.
  • postProcessAfterInitialization: Spring calls this method after any bean initialization callbacks.

Let us start by creating a bean, named BookBean.

package guru.springframework.springbeanlifecycle.beanpostprocessor.domain;

public class BookBean {
private String bookName;

public BookBean() {
System.out.println("Constructor of BookBean called !! ");

}

public BookBean(String bookName) {
this.bookName = bookName;
}

public String getBookName() {
return bookName;
}

public void setBookName(String bookName) {
this.bookName = bookName;
}

@Override
public String toString() {
return "BookBean{" +
"bookName='" + bookName + '\'' +
'}';
}
}

Next, we will create the BookBeanPostProcessor.

The code for BookBeanPostProcessor is this.

package guru.springframework.springbeanlifecycle.beanpostprocessor.domain;

import guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class BookBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Post Process Before Initialization method is called : Bean Name " + beanName);
return bean; }

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Post Process After Initialization method is called : Bean Name " + beanName);

return bean;
}
}

The preceding code implements the BeanPostProcessor interface and overrides the postProcessBeforeInitialization() and postProcessAfterInitialization() methods.

Spring calls the postProcessBeforeInitialization() method after calling the methods of the aware interfaces.

Spring calls the postProcessAfterInitialization() method after any bean initialization callbacks, such as InitializingBean’s afterPropertiesSet or a custom init-method. We will discuss both going ahead.

At runtime, Spring will inject the new bean instance and the name of the bean to both the methods.

Next, we will define BookBean and BookBeanProcessor as beans in the XML configuration.

The configuration code is this.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="bookBeanPost" class="guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean">
       <property name="bookName" value="Gone with the Wind"></property>
   </bean>
   <bean id="bookBeanPostProcessor"
         class="guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBeanPostProcessor"/>
</beans>

The main class to test our BeanPostProcessor is this.

package guru.springframework.springbeanlifecycle;

import guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SpringBeanLifecycleApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBeanLifecycleApplication.class, args);
// -------beanpostprocessor------
ApplicationContext context4 =
new ClassPathXmlApplicationContext("beans.xml");
BookBean bookBean = (BookBean) context4.getBean("bookBeanPost");
((AbstractApplicationContext) context4).registerShutdownHook();
}
}

The output on running the main class is this.

 
Constructor of BookBean called !!
Post Process After Initialization method is called: Bean Name bookBeanPost
Post Process Before Initialization method is called: Bean Name bookBeanPost

InitializingBean and DisposableBean Callback Interfaces

Spring provides the following two callback interfaces:

  • InitializingBean: Declares the afterPropertiesSet() method which can be used to write the initialization logic. The container calls the method after properties are set.
  • DisposableBean: Declares the destroy() method which can be used to write any cleanup code. The container calls this method during bean destruction in shutdown.

Let’s write a bean that implements the InitalizingBean and DisposableBean interfaces.

The code of the Book bean is this.

package guru.springframework.springbeanlifecycle.callbackinterfaces.domain;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Book implements InitializingBean, DisposableBean {
private String bookName;
public Book() {
System.out.println("Constructor of Book bean is called !! ");
}

@Override
public void destroy() throws Exception {
System.out.println("Destroy method of Book bean called !! ");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet method of Book bean is called !! ");
}
public Book(String bookName) {
this.bookName = bookName;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
'}';
}
}

The preceding Book bean implements the InitializingBean and DisposableBean interfaces and overrides their afterPropertiesSet() and destroy() method.

Next, we will write the bean configuration to define the Book bean.

The code of the beans.xml file is this.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!--     callbackinterfaces-->
   <bean id="bookBean" class="guru.springframework.springbeanlifecycle.callbackinterfaces.domain.Book">
       <property name="bookName" value="Believe in Yourself"/>
   </bean>
</beans>

The main class is this.

package guru.springframework.springbeanlifecycle;

import guru.springframework.springbeanlifecycle.callbackinterfaces.domain.Book;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SpringBeanLifecycleApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBeanLifecycleApplication.class, args);

// -------callbackinterfaces-------
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
Book book = (Book) context.getBean("bookBean");
System.out.println(book.getBookName());
((AbstractApplicationContext) context).registerShutdownHook();

}

}

The preceding code retrieves the Book bean from the ApplicationContext and prints out the value of the bookName property.

The output on running the main class is this.

 
Constructor of Book bean is called !!
afterPropertiesSet method of Book bean is called !!
Believe in Yourself
destroy method of Book bean is called !!

As you can note in the output the afterPropertiesSet() method got called first.

Custom Init and Destroy Method

While declaring bean in XML configuration, you can specify the init-method and destroy-method attributes in the tag. Both the attributes specify custom methods in the bean class.

The method declared in the init-method attribute is called after Spring initializes bean properties through setter or constructor arguments. You can use this method to validate the injected properties or perform any other tasks.

Spring calls the method declared in the destroy-method attribute just before the bean is destroyed.

Let’s use the custom init and destroy methods in a bean, named BookCustomBean.

The code for BookCustomBean; is this.

package guru.springframework.springbeanlifecycle.custominitanddestroy.domain;

public class BookCustomBean {
private String bookName;

public BookCustomBean() {
System.out.println("Constructor of BookCustomBean bean is called !! ");
}

public void customDestroy() throws Exception {

System.out.println("Custom destroy method of BookCustomBean called !! ");
}

public void customInit() throws Exception {
System.out.println("Custom Init method of BookCustomBean called !! ");
}

public BookCustomBean(String bookName) {
this.bookName = bookName;
}

public String getBookName() {
return bookName;
}

public void setBookName(String bookName) {
this.bookName = bookName;
}

@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
'}';
}

}

In the preceding code, customInit and customDestroy are regular methods that prints output messages.

Next, we’ll write the bean configuration, beans.xml.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!--    Declare custom init and destroy methods-->
   <bean id="customLifeCycleBookBean"
         class="guru.springframework.springbeanlifecycle.custominitanddestroy.domain.BookCustomBean"
         init-method="customInit"
         destroy-method="customDestroy">
       <property name="bookName" value="Life and Laughing"></property>
   </bean>

</beans>

In the preceding code, Line 11 – Line 12 uses the init-method and destroy-method attributes with the values, customInit and customDestroy.

The code of the main class is this.

package guru.springframework.springbeanlifecycle;

import guru.springframework.springbeanlifecycle.custominitanddestroy.domain.BookCustomBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SpringBeanLifecycleApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBeanLifecycleApplication.class, args);
// -------custominitanddestroy------
ApplicationContext context3 =
new ClassPathXmlApplicationContext("beans.xml");

BookCustomBean bookCustomBean = (BookCustomBean) context3.getBean("customLifeCycleBookBean");
((AbstractApplicationContext) context3).registerShutdownHook();
}
}

The preceding code loads the XML configuration and tests the init-method and destroy-method.

The output on running the preceding code is this.

 
Constructor of BookCustomBean bean is called !!
Custom Init method of BookCustomBean called !!
Custom destroy method of BookCustomBean called !!

Summary

All Spring beans go through a specific life cycle, and as we have seen, there’s
actually, a lot goes on under the hood. Most of this is handled by the framework and as a Spring developer, you will seldom require to get into it that often. However, as you get into more and more complex applications with the Spring framework, at times you have to be aware of what goes on during a bean lifecycle.

I personally don’t prefer using the InitializingBean and DisposableBean interfaces. Primarily because it tight couples your code to Spring. A better approach is specifying the init-method and destroy-method attributes in your bean configuration file.

About SFG Contributor

Staff writer account for Spring Framework Guru

    You May Also Like