Spring Bean Lifecycle
2 CommentsLast Updated on July 12, 2020 by Simanta
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
: 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 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’safterPropertiesSet
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 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 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.
The source code for this post can be found here on GitHub.
Yuri
Thank you. Very helpfull.
But I have a question:
Why does GURU use in the applications spring boot import?
That works without:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
and without:
@SpringBootApplication
Gregory P
Why do you put socks on before you put on your shoes?
The shoes work without the socks.