Spring Bean Lifecycle
Check out this post to learn more about the Spring bean lifecycle.
Join the DZone community and get the full member experience.
Join For FreeThe 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
The figure below 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
: ProvidessetBeanFactory()
, 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 theApplicationContext
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 lifecycle 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 asInitializingBean
’safterPropertiesSet
or a custominit
-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 theafterPropertiesSet()
method which can be used to write the initialization logic. The container calls the method after properties are set.DisposableBean
: Declares thedestroy()
method which can be used to write any clean up 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.
- The constructor of
Book
bean is called !! afterPropertiesSet
method ofBook
bean is called !!- Believe in Yourself
destroy
method ofBook
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 ofBookCustomBean
called !! - Custom
destroy
method ofBookCustomBean
called !!
Summary
All Spring beans go through a specific lifecycle, and as we have seen, there’s actually a lot that 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 tightly couples your code to Spring. A better approach is specifying the init
-method and destroy
-method attributes in your bean configuration file.
Further Reading
Published at DZone with permission of John Thompson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments