Spring Bean Lifecycle: Best Practices and Pitfalls

An important part of the Spring IoC container is that beans should be constructed in such a way that they receive notifications at certain points in their life cycle. This helps in intercepting and performing additional logic when these lifecycle events happen.

In this tutorial, we will learn about bean life cycle stages in Spring which are divided into two categories: post-initialization and pre-destruction callbacks. We will also learn to customize the bean life cycle events using XML configuration as well as Java annotation configuration.

1. What is Lifecycle of a Bean in Spring?

The lifecycle of a bean in the Spring Framework refers to the series of steps and methods that a bean goes through from its instantiation to its eventual destruction.

The lifecycle of a bean is managed by the Spring container (BeanFactory) which is responsible for creating, configuring, and destroying beans according to the defined bean scope (singleton, prototype, etc.). It follows the series of steps as described in the below image, in sequence.

Spring Bean Life Cycle
Spring Bean Life Cycle

As part of the bean lifecycle, the container may also be required to perform some pre-and-post initialization steps to get the bean into a usable state.

Similarly, when the bean is no longer required, it will be removed from the IoC container. Like the initialization phase, the Spring framework may need to perform pre-and-post destruction steps to free the other system resources.

2. Bean Lifecycle Callback Methods

To execute some custom code, the bean factory allows to execute the callback methods, which can be categorized broadly into two groups:

  • Post-initialization callback methods
  • Pre-destruction callback methods

In Spring, the initialization life-cycle callback methods will be called on objects regardless of bean scope, while for beans with prototype scope, the destruction life-cycle callback methods will not be called.

It’s the responsibility of the application to manage the destruction and cleanup of prototype beans, as Spring does not keep a reference to prototype beans for automatic destruction.

2.1. Post-Initialization Callbacks

Most often, but not limited to, the initialization callbacks are used to perform the dependency checks that are not possible in the constructor. There may be other usecases such as if a bean to run scheduled tasks then the initialization callback is the ideal place to start the scheduler.

The post-initialization callback method cannot accept any arguments. Although, it can return any type which Spring will ignore anyways.

We can even define all initialization mechanisms on the same bean instance. It is allowed in Spring. In this case, Spring invokes the callbacks in the following order:

  • Method annotated with @PostConstruct
  • Method InitializingBean.afterPropertiesSet()
  • Method specified in the @Bean definition

2.2. Pre-destruction Callbacks

When the application shuts down, Spring calls the ConfigurableBeanFactory.destroySingletons() method which provides us a chance to clean up any resources that the beans might be holding open, thus allowing the application to shut down gracefully. When the destroySingletons() is called, the beans get the notifications via pre-destruction callback methods (similar to post-initialization callbacks).

The destruction callback is often used in conjunction with the initialization callback. In many cases, we create and configure a resource in the initialization callback and then release the resource in the destruction callback.

Notice the method name destroySingletons(). It hints that beans with other scopes than singleton do not have their life cycle fully managed by Spring.

As with the case of bean creation, the order of method invocations in case of multiple bean destruction callbacks is:

  • Method annotated with @PreDestroy
  • Method DisposableBean.afterPropertiesSet()
  • Method specified in the @Bean definition

3. Different Ways to add the Bean Lifecycle Callbacks

Spring framework provides the following ways for controlling the lifecycle events of a bean. These are broadly categorized into three categories:

  • Interface-based Lifecycle Management can be achieved by implementing
    • InitializingBean and DisposableBean interfaces
    • *Aware interfaces for specific behavior
  • Method-based Lifecycle Management is achieved by adding custom init() and destroy() methods in the bean class and declaring them in the bean configuration file.
  • Annotation-based Lifecycle Management is achieved by adding JSR-250 @PostConstruct and @PreDestroy annotated methods in the bean class.

Overall, the choice of which mechanism you use for receiving life-cycle notifications depends on the application requirements.

Let’s learn about each mechanism in more detail.

4. Implementing the InitializingBean and DisposableBean Interfaces

The org.springframework.beans.factory.InitializingBean interface allows a bean to perform initialization work after all necessary properties on the bean have been set by the container. The InitializingBean interface specifies a single method afterPropertiesSet() which Spring automatically calls after the bean’s properties have been set.

import org.springframework.beans.factory.InitializingBean;

public class MyBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // Custom initialization logic
    }
}

Similarly, implementing the org.springframework.beans.factory.DisposableBean interface allows a bean to get a callback during bean destruction, such as when the application context is closed.

The DisposableBean interface specifies a single method destroy() which Spring automatically calls automatically.

import org.springframework.beans.factory.DisposableBean;

public class MyBean implements DisposableBean {

    @Override
    public void destroy() {
        // Custom destruction logic
    }
}

5. *Aware Interfaces to Add Specific Behavior

Spring offers a range of interfaces that allow the beans to indicate to the container that they require a particular infrastructure dependency. Each of these Aware interfaces will require us to implement a method to inject the dependency in the bean.

Some commonly used Aware interfaces are:

Aware interfaceMethod to OverridePurpose
ApplicationContextAwaresetApplicationContext()Sets the ApplicationContext that the bean runs in.
ApplicationEventPublisherAwaresetApplicationEventPublisher()Sets the ApplicationEventPublisher that the bean runs in.
BeanNameAwaresetBeanName()Sets the name of the bean in the bean factory.
LoadTimeWeaverAwaresetLoadTimeWeaver()Sets the LoadTimeWeaver of this object’s containing ApplicationContext.
MessageSourceAwaresetMessageSource()Sets the MessageSource that this bean runs in.
NotificationPublisherAwaresetNotificationPublisher()Sets the NotificationPublisher instance for the current managed resource instance.
ResourceLoaderAwaresetResourceLoader()Sets the ResourceLoader that this object runs in.
ServletContextAwaresetServletContext()Sets the ServletContext that this object runs in.

A simple Java bean definition to demonstrate how to use the ‘Aware‘ interfaces.

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class DemoBean implements ApplicationContextAware {

	private ApplicationContext ctx;

	@Override
	public void setApplicationContext(ApplicationContext ctx) throws BeansException {
		this.ctx = ctx;
	}

	//Use the context in other bean methods
}

6. Custom init() and destroy() Methods

We can add the default init() and destroy() methods in two ways:

  • Local definitions applicable to a single bean
  • Global definitions applicable to all beans defined in whole beans context

6.1. Bean-specific Init and Destroy Methods

The local init() and destroy() methods can be configured to a specific bean as in the given XMl Configuration example.

<beans>
	//...
	<bean id="demoBean" class="com.howtodoinjava.task.DemoBean"
	    init-method="customInit"
	    destroy-method="customDestroy"></bean>
	//...
</beans>

The same configuration can be done using Java @Bean annotation as follows;

@Configuration
public class AppConfig {

    @Bean(initMethod = "customInit", destroyMethod = "customDestroy")
    public DemoBean demoBean() {
        return new DemoBean();
    }
}

Then the init and destroy methods can be defined in the bean as follows:

public class DemoBean {

    // Properties and methods of your bean

    // Custom initialization method
    public void customInit() {
        // Custom initialization logic
    }

    // Custom destruction method
    public void customDestroy() {
        // Custom destruction logic
    }
}

6.2. Global-scoped Init and Destroy Methods

We can define the init and destroy methods at the global level as well. The container will invoke the global init and destroy methods for all bean definitions using the attributes ‘default-init-method‘ and ‘default-destroy-method‘.

Global overrides are helpful when we have a pattern of defining common method names such as init() and destroy() for all the beans consistently. This feature helps us not to duplicate the init and destroy methods for all beans, separately.

<beans default-init-method="customInit" default-destroy-method="customDestroy">   
	//...
	<bean id="demoBean" class="com.howtodoinjava.task.DemoBean"></bean>
	<bean id="newDemoBean" class="com.howtodoinjava.task.NewDemoBean"></bean>
	<bean id="anotherDemoBean" class="com.howtodoinjava.task.AnotherDemoBean"></bean>
	//...
</beans>

When using global init and destroy methods, all interested beans must write the customInit() and customDestroy() methods in the class definition as in the below example.

public class DemoBean {

	public void customInit() {
		System.out.println("Method customInit() invoked...");
	}

	public void customDestroy()	{
		System.out.println("Method customDestroy() invoked...");
	}
}

7. @PostConstruct and @PreDestroy Annotations

From Spring 2.5 onwards, we can use the @PostConstruct and @PreDestroy annotations for specifying the bean life cycle methods.

  • @PostConstruct annotated method will be invoked after the bean has been constructed using the default constructor and just before its instance is returned to the requesting object.
  • @PreDestroy annotated method is invoked just before the bean is about to be destroyed inside the bean container.

Java program to show usage of annotation configuration to control using annotations.

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

public class DemoBean {

  @PostConstruct
  public void customInit()
  {
    System.out.println("Method customInit() invoked...");
  }

  @PreDestroy
  public void customDestroy()
  {
    System.out.println("Method customDestroy() invoked...");
  }
}

8. Registering a Shutdown Hook is Necessary

When we run the demo programs listed above, we expect the program to print the messages written in post-init and pre-destroy callback methods. Let’s try it.

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
  public static void main(String[] args) {

    var applicationContext = new AnnotationConfigApplicationContext(Config.class);
  }
}

The program output prints only the init method message. The destroy callback method message is not printed.

Method customInit() invoked...

This is because that to call the pre-destry callbacks, we must call the applicationContext.close() method before the application is closed. But when an application can be closed/stopped/killed in several ways then it becomes harder to place the above statement.

Fortunately, there is a solution. Java allows to register a shutdown hook which is a thread that is executed just before the application shuts down.

To take advantage of this mechanism, we must invoke the AbstractApplicationContext’s registerShutdownHook() method anytime before the application is stopped. Let’s try this in our demo program.

var applicationContext = new AnnotationConfigApplicationContext(Config.class);
applicationContext.registerShutdownHook();

The program output prints both messages this time. This is te expected behavior.

Method customInit() invoked...
Method customDestroy() invoked...

9. Conclusion

In this Spring tutorial, we explored the four different ways of controlling the lifecycle of Spring beans. Each of these methods offers flexibility and customization options, and the choice depends on the specific requirements and the level of control we need over the beans’ lifecycles.

It is recommended to learn above discussed life cycle events in detail as it is a commonly asked Spring interview question.

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Hi Lokesh, I have one doubt. I have created a small project with 1 bean class, main class and context.xml file. What are the default interfaces implemented by bean behind the scenes if i wont implement any of them manually? Will it implements all the interfaces listed above or any particular mandatory interfaces it will implement ? Please help me in this, as i am stuck in this doubt without moving forward.

    Reply
    • By default, a bean does not implement any of above interfaces. Above interfaces are used for executing code blocks on specific lifecycle event on a bean, and so these are used as and when needed only.

      Reply
      • Thanks a lot Lokesh. I am clear now, as u said they are used when needed only. But,one doubt still persists. Spring container handles each and every beans life cycle defined in context.xml file. What are those mandatory life cycle methods( and these methods belongs to which interfaces ?) called by the Spring container. Example :- I have a single bean class, context file and Test class, and when i run Test class, which are the default methods called on the bean.
        Please bear my questions, which would help me lot :-).

        Reply
        • By default, you can have two methods “init-method” and “destroy-method”. These methods then must be defined inside bean class. Spring framework internally uses reflection to invoke these methods.

          Reply
  2. Nice Explanation. But could you Please discuss more about “Other Aware Interfaces…” and implementation of all these type.

    Thanks
    Tofek

    Reply
  3. Hi,
    Thanks for the above information .It is really helpful.

    I had a requirement in my project, need to run the database scripts through liquibase. There is existing framework to check the change logs and run the change logs . So, just bean definition for this java class is enough to run the liquibase script changes for the entire project . This i have done as below
    <bean id = "lbase" class="com.*.*.**.<> datasource=”datasource” />

    Now, I had another webservice client configuration class which handles the code to read the webservice url and username from database.And the table will be created and loaded with data by the above defined liquibase bean. I have defined this webservice configuration bean as

    The order of defining in spring context file is firstly,liquibase related bean and then wsClientConfig bean is defined.
    now, the doubt is -among these two, which bean is initialized first and which is next?
    For me, error is coming as- no table or view exists while fetching the details of webservice fails.

    Please help in handling this scenario. Thank you

    Reply
  4. I have a doubt.Spring works on the top of the underline java its means JVM .Why spring beans required one more life cycle it means that already jvm will have a life cycle

    Reply
    • JVM does not manage it’s bean’s lifecyle. It’s container for all objects, and it checks which objects are live or dead. JVM never tries to modify the state or behavior of an object. objects are just created inside JVM and cleanup by it when they are no longer referred from other objects. That’s it !!

      Spring bean life cycle is different thing. Here you want to change the state/behavior of bean (or application) whenever an object get’s created or destroyed, or in some specific events. Please note that these events are not JVM triggered events. They are simple “callback methods” when spring’s IOC framework create those bean instances or destroy them.

      Reply
  5. Hi, Lokesh Thanks a lot for this meaningful blog . i have one question which is related to beans life cycle i understood when public void afterPropertiesSet() has called but i have one confusion when public void destroy() will call and who call public void destroy() . and who is responsible to destroy spring bean?

    Reply
  6. Hai am very new to spring .I am getting some problem in Spring bean lifecycle methods.Like
    the out put is
    constructor
    setId
    setName
    This is init-method
    Jul 10, 2014 9:58:35 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
    INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2a5330: defining beans [st]; root of factory hierarchy
    constructor
    setId
    setName
    before
    This is init-method
    after
    This is bussinessmethod
    means constructor and setters methods are called two times.what happen will anyone suggest me

    Reply
    • The reason is – spring aop is proxy-based, i.e. the container creates “raw” bean instance and proxy for it (created at runtime via subclassing original bean class) if the bean is affected by AOP rules. So, when proxy object is created, it calls superclass constructor/setters and it looks as two instances are created.

      You notice that “before” and “after” methods are not called because they are run only when actual instance is created. It’s contract provided by Spring’s annotations @PostConstruct and @PreDestroy; OR “init-method” and “destroy-method” attributes of the bean element in configuration file.

      Reply
    • Thanks for your reply.I understand well.Small thing is here am not using Aop concept.i just tries it by implenting BeanPostProcessor interface.And one more thing is if i add bean scope is “singleton” then BeforeInitialization and afterInitialization methods are not called.Plz explain me abt is there any internal relation between bean scopes and BeanPostProcessor interface methods. in BeanLife management.plz reply me.

      Reply
  7. Hi, Thanks for your blog. I have one requirement in my current project like i need to start a new thread in my spring application. In that thread i need to call one of the beans methods. Would you please suggest.

    Reply
    • If the object (thread instance) that needs access to the container is a bean in the container, just implement the BeanFactoryAware or ApplicationContextAware interfaces.

      If an object outside the container needs access to the container, you must use singleton pattern for the spring container. Create this singleton bean in application startup, and set the application context reference there in a variable. Now whenever you need a bean, get the context via singleton class, and lookup the bean from application context.

      Reply
  8. Hi, Thanks a lot for this meaningful blog, I have in a problem please help me to solve it.
    Problem: How to prevent serialization in java if the class has implemented Serializable interface. Problem might be like this,
    Class A implements Seralizable{
    }
    class B extends A{
    }

    How to prevent class B to serialize if it have behavior of serialization if someone going to serialize. Please give me innovative solution.

    Hope i will hear you soon.

    Regard’s
    Santosh Srivastava

    Reply
    • Not possible. Reason is that in java super class can not see the internals of child classes. Only child classes can access super class’s fields and methods. Anyway, what kind of design is this in which super class is serializable but child classes are not?? Please refer to “Liskov’s Substitution Principle” in https://howtodoinjava.com/best-practices/solid-principles/

      Anyways, you have following options:

      1) Make super class final so that no one can extend it? [I know it is not what you expect].
      2) Make super class’s field transient, so that if B is serialized then it will have only its state saved.

      Anybody came across this conversation, feel free to engage. :)

      Reply
      • In Java, if the super class of a class is implementing Serializable interface, it means that it is already serializable. Since, an interface cannot be unimplemented, it is not possible to make a class non-serializable. However, the serialization of a new class can be avoided. For this, writeObject () and readObject() methods should be implemented in your class so that a Not Serializable Exception can be thrown by these methods. And, this can be done by customizing the Java Serialization process. Below the code that demonstrates it

        class MySubClass extends SomeSerializableSuperClass 
        {
        	private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        	throw new NotSerializableException(“Can not serialize this class”);
        	}
        	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        		throw new NotSerializableException(“Can not serialize this class”);
        	}
        	private void readObjectNoData()	throws ObjectStreamException; {
        		throw new NotSerializableException(“Can not serialize this class”);
        	}
        }
        
        Reply

Leave a Comment

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode