April 18, 2022

Spring Bean Lifecycle Callback Methods

In Spring framework it is the Spring container that is responsible for instantiating beans, setting bean properties, wiring dependencies and managing the complete bean lifecycle from its instantiation to the time bean is destroyed.

Spring bean lifecycle callbacks

To interact with the container’s management of the bean lifecycle, in order to customize the nature of a bean, Spring Framework provides a number of interfaces which can be categorized into following categories-

  • Lifecycle Callbacks
  • ApplicationContextAware and BeanNameAware
  • Other Aware Interfaces

Spring bean lifecycle callback methods

Spring framework provides callback methods that can be configured to be called-

  1. After the bean is initialized i.e. post initialization call back methods.
  2. Before the bean is destroyed i.e. pre destruction call back methods.

In Spring bean lifecycle post initialization call back methods are-

  • The InitializingBean callback interface provides a method afterPropertiesSet() that can be used for initialization work.
  • Methods annotated with @PostConstruct
  • Custom init() method

In Spring bean lifecycle pre destruction call back methods are-

  • The DisposableBean callback interface provides a method destroy() which lets a bean get a callback when the container that contains it is destroyed.
  • Methods annotated with @PreDestroy
  • Custom destroy() method

Spring bean lifecycle callback methods execution order

If multiple lifecycle callbacks are configured for the same bean then different initialization methods are called as follows-

  1. Methods annotated with @PostConstruct
  2. afterPropertiesSet() as defined by the InitializingBean callback interface
  3. A custom configured init() method

Destroy methods are called in the following order:

  1. Methods annotated with @PreDestroy
  2. destroy() as defined by the DisposableBean callback interface
  3. A custom configured destroy() method

Callbacks for ApplicationContextAware and BeanNameAware interfaces is invoked after setting of bean properties but before an initialization callback such as InitializingBean or a custom init-method.

Following image shows the call back method flow after the bean is instantiated.

spring bean lifecycle instantiation

Following image shows the call back method flow before the bean is destroyed.

bean lifecycle destruction callback

InitializingBean and DisposableBean callback interfaces

The org.springframework.beans.factory.InitializingBean interface has a single method afterPropertiesSet(). By implementing this method you provide a post initialization call back method that let bean perform initialization work after the container has set all necessary properties on the bean.

The org.springframework.beans.factory.DisposableBean interface has a single method destroy(). By implementing this method you provide a pre destruction call back method that is called when the container that contains the bean is destroyed.

As per Spring docs it is not recommended to use the InitializingBean and DisposableBean callback interfaces as it unnecessarily couples the code to Spring. Using annotations @PostConstruct and @PreDestroy or custom init() and destroy() methods should be preferred.

InitializingBean and DisposableBean interfaces example

In the example there is a class OrderServiceImpl which has a dependency on Store. OrderServiceImpl class implements InitializingBean and DisposableBean interfaces and provides the callback methods.

public interface OrderService {
  public void buyItems();
}
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

public class OrderServiceImpl implements OrderService, InitializingBean, DisposableBean {
  private IStore store;
  @Autowired
  public OrderServiceImpl(IStore store){
    this.store = store;
  }
  // post initialization callback
  public void afterPropertiesSet() throws Exception {
    System.out.println("In afterPropertiesSet method for bean initialization work");
  }
  // pre destruction callback
  public void destroy() throws Exception {
    System.out.println("In destroy() method, cleaning up resources");
  }
  public void buyItems() {
    store.doPurchase();
  }
}
public interface IStore {
  public void doPurchase();
}
public class RetailStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Retail Store");
  }
}
XML Configuration
<?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.xsd">
    
  <context:annotation-config />
  <!-- Store bean -->
  <bean id="store" class="com.knpcode.springproject.service.RetailStore" />

  <!-- OrderServiceImpl bean with store bean dependency -->
  <bean id="orderBean" class="com.knpcode.springproject.service.OrderServiceImpl" />
</beans>
You can use the following class with main method to read the configuration and call the bean method.
public class App {
  public static void main( String[] args ){
    // create context using configuration
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
    OrderService order = context.getBean("orderBean", OrderServiceImpl.class);
    order.buyItems();
    context.close();
  }
}
Output
10:58:24.120 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
10:58:24.128 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
10:58:24.156 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'store'
10:58:24.187 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderBean'
10:58:24.287 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderBean' via constructor to bean named 'store'
In afterPropertiesSet method for bean initialization work
Doing purchase from Retail Store
In destroy() method, cleaning up resources

As you can see from the output first beans are instantiated and dependencies are wired after that afterPropertiesSet() callback method is called. When the container is closed at that time destroy() method is called on the bean.

Custom init and destroy methods in Spring bean lifecycle

You can also configure custom init and destroy callback methods using init-method and destroy-method attributes of the element. For example

<bean id="orderBean" class="com.knpcode.springproject.service.OrderServiceImpl" 
	      init-method="initMethod" destroy-method="destroyMethod" />
Default init and destroy methods

You can also configure custom init and destroy callback methods globally to be called for all the beans defined with in element. That way you don't need to configure init and destroy attributes with each bean definition. At the same time you do need to have same method name for post initialization and pre destruction in each bean class. For example-

<beans default-init-method="init" default-destroy-method="destroy">
  <bean id="orderBean" class="com.knpcode.springproject.service.OrderServiceImpl" 
    <property name="store" ref="storeBean" />
  </bean>
  <bean>.....</bean>
  ....
  ....
</beans>

@PostConstruct and @PreDestroy annotations in Spring bean lifecycle

A method annotated with @PostConstruct is considered a post initialization method where as a method annotated with @PreDestroy is considered a predestruction method.

Spring bean lifecycle callback methods example

In this example we’ll see use of all the three initialization and disposable methods that way you can check the order in which these methods are called.

Note that for using @PostConstruct and @PreDestroy annotations javax annotation API is needed. We need to add dependency for this explicitly Java 9 onward.

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

Here is a class which has all the types of initialization and disposable methods.

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class OrderServiceImpl implements OrderService, InitializingBean, DisposableBean {
  private IStore store;
  @Autowired
  public OrderServiceImpl(IStore store){
    this.store = store;
  }
  // post initialization callback
  public void afterPropertiesSet() throws Exception {
    System.out.println("In afterPropertiesSet method for bean initialization work");
  }
  // pre destruction callback
  public void destroy() throws Exception {
    System.out.println("In destroy() method, cleaning up resources");
  }
  public void initMethod() {
    System.out.println("call init method");
  }
  public void destroyMethod() {
    System.out.println("call destroy method");
  }
  @PostConstruct
  public void initAnnotationMethod() {
    System.out.println("call init method for post construct");
  }
  @PreDestroy
  public void destroyAnnotationMethod() {
    System.out.println("call destroy method for pre destroy");
  }
  public void buyItems() {
    store.doPurchase();
  }
}
Output
call init method for post construct
In afterPropertiesSet method for bean initialization work
call init method
Doing purchase from Retail Store
call destroy method for pre destroy
In destroy() method, cleaning up resources
call destroy method

As you can see first the methods annotated with @PostConstruct and @PreDestroy annotations are called. After that afterPropertiesSet() and destroy() methods are called and at the last custom configured init() and destroy() methods are called.

Aware interfaces in Spring framework

In Spring framework there are many Aware callback interfaces that let beans indicate to the container that they require a certain infrastructure dependency.

Some of the most important Aware interfaces are-

  • ApplicationContextAware- This interface has setApplicationContext() method which injects the ApplicationContext dependency into the bean. Using this ApplicationContext reference beans can programmatically manipulate the ApplicationContext that created them.
  • BeanNameAware- This interface has setBeanName() method. Class that implements the org.springframework.beans.factory.BeanNameAware interface is provided with a reference to the name defined in its associated object definition.
  • BeanFactoryAware- By implementing this interface bean will be injected with the declared BeanFactory. Using it you can get the bean definition and its attributes.
  • ServletConfigAware- This interface is valid only in in a web-aware Spring ApplicationContext and injects the Current ServletConfig the container runs in.
  • ServletContextAware- This interface is valid only in in a web-aware Spring ApplicationContext and injects the Current ServletContext the container runs in.

Spring Aware interfaces example

In the following example bean class implements ApplicationContextAware, BeanNameAware and BeanFactoryAware interfaces.

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;

public class ExampleBean implements ApplicationContextAware, BeanNameAware, BeanFactoryAware{

  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("In setBeanFactory method");
    // Getting another bean and calling its method
    OrderService orderService = (OrderService)beanFactory.getBean("orderBean");
    orderService.buyItems();		
  }

  public void setBeanName(String name) {
    System.out.println("In setBeanName method");
    System.out.println("Bean's name- " + name);		
  }

  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    System.out.println("In setApplicationContext");
    OrderService orderService = (OrderService)applicationContext.getBean("orderBean");
    orderService.buyItems();
  }
}
Output
In setBeanName method
Bean's name- exampleBean
In setBeanFactory method
14:33:52.227 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderBean'
14:33:52.300 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderBean' via constructor to bean named 'store'
call init method for post construct
In afterPropertiesSet method for bean initialization work
call init method
Doing purchase from Retail Store
In setApplicationContext
Doing purchase from Retail Store
Doing purchase from Retail Store
call destroy method for pre destroy
In destroy() method, cleaning up resources
call destroy method

That's all for the topic Spring Bean Lifecycle Callback Methods. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment