Java

Spring Bean Lifecycle

In this article, we will learn the Java bean object lifecycle and some useful methods to perform the operations during its life cycle phase such as initialization and destroying the bean object.Each bean object is managed by the Spring IOC and the lifecycle starts when the container (IOC) starts based on the configuration metadata.

The Spring Bean lifecycle has numerous steps such as initialization, running, and destroy. We can perform our custom task in between these phases for testing/debugging or other utility purposes.

For example, if we want to perform some tasks at the start of the bean and some clean up related tasks at the end, we can do that at the bean initialization or before destroying the bean object, respectively. It makes the application more functional and easier to debug as well.

For this purpose, Spring provides some predefined interfaces and annotations that makes our task easy. There are two ways to perform these tasks:

First, we start with the interfaces and then look into the annotations.

Interfaces for Java Bean Lifecycle

Spring has two interfaces, InitializingBean and DisposableBean, that contains several utility methods to perform the tasks during the bean lifecycle.

To perform the initializing task, we can use afterPropertiesSet() method of the InitializingBean interface similarly for cleaning resources. Before destroying the bean object, we can use the destroy() method of the DisposableBean interface.

Let’s start with running examples.

Implementing Lifecycle methods using Interfaces

In this case, first, we create a maven-based spring application and then create a bean Employee. This bean class implements both interfaces that we discussed. Now, we must implement their methods as well.

The afterPropertiesSet() method is used to perform the initialization task and the destroy() method to perform the task before destroying the bean object. See the source code of the Employee bean class.
// employee.java

packagecom.linuxhint.beans;
importorg.springframework.beans.factory.DisposableBean;
importorg.springframework.beans.factory.InitializingBean;
importorg.springframework.stereotype.Component;
@Component("emp")
publicclass Employee implementsInitializingBean, DisposableBean {
   
    privateintid;
    private String name;

    public String getName() {
        returnname;
    }

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicintgetId() {
        returnid;
    }

    publicvoidsetId(intid) {
        this.id = id;
    }

    @Override
    publicvoidafterPropertiesSet() throws Exception {
        System.out.println("Initializing tasks done...");
       
    }

    @Override
    publicvoiddestroy() throws Exception {
        System.out.println("Cleaning tasks done... \nBean object destroyed!");
       
    }  
}

This is the main class where we instantiate the bean and call its getters and setters method.

// SpringApp.java

package com.linuxhint;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.linuxhint.beans.Employee;
public class SpringApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        Employee emp = (Employee) ac.getBean("emp");
        emp.setName("Rohan");
        System.out.println("Bean Executing...");
        String name = emp.getName();
        System.out.println("Name: "+name);
        ac.close();
    }
}

// SpringConfig.java

packagecom.linuxhint;

importorg.springframework.context.annotation.ComponentScan;
importorg.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.linuxhint.*")
publicclassSpringConfig {

}

// pom.xml

This the XML file that contains all the dependency code for the application.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.linuxhint</groupId>

<artifactId>springapp</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>springapp</name>

<description>a simple spring app</description>

<dependencies>

    <dependency>

       <groupId>org.springframework</groupId>

       <artifactId>spring-core</artifactId>

       <version>${spring.version}</version>

    </dependency>

    <dependency>

       <groupId>org.springframework</groupId>

       <artifactId>spring-context</artifactId>
       
       <version>${spring.version}</version>

    </dependency>

</dependencies>

<properties>

<spring.version>5.2.8.RELEASE</spring.version>

</properties>

</project>

Run the Project

After creating the project, it’s time to run the application to check whether all the files are working fine and the application is working as expected. This application prints the following output to the console window:

Output:

Initializing tasks done...

Bean Executing...

Name: Rohan

Cleaning tasks done...

Bean object destroyed!

Notice: We did not call the lifecycle methods but implicitly called the Spring Container (IOC).

Implementing Lifecycle Methods Using Annotations

This is the modern approach where we use the annotations inplace of interfaces. That means no more interfaces are needed to be implemented by the bean class. We just need to mark the methods to call using the @PostConstruct and @PreDestroy annotations.

The @PostConstruct annotation is called at initialization time while the @PreDestroy is called just before destroying the bean object.

Note: Before using the annotations, we must add the following dependency to our project’s pom.xml file.

This package is not present in the default spring package. So, for Java 9 and higher versions, put these JARs in the pom.xml file:

<dependency>

<groupId>javax.annotation</groupId>

<artifactId>javax.annotation-api</artifactId>

<version>1.3.2</version>

</dependency>

Spring does not provide any predefine method as in the interface case. We are free to create any method having any name. We just need to mark them with the annotations to specify when to call them by the IOC container.

Here, we create the initWork() method to perform the task at initial level and the endWork() method to perform the task before destroying the bean object.

Update the Employee.java file according to the following source code:

// Employee.java

packagecom.linuxhint.beans;
importjavax.annotation.PostConstruct;
importjavax.annotation.PreDestroy;
importorg.springframework.stereotype.Component;
@Component("emp")
publicclass Employee {
   
    privateintid;
    private String name;


    public String getName() {
        returnname;
    }

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicintgetId() {
        returnid;
    }

    publicvoidsetId(intid) {
        this.id = id;
    }

    @PostConstruct
    publicvoidinitWork() {
        System.out.println("Initializing tasks done...");
       
    }

    @PreDestroy
    publicvoidendWork() {
        System.out.println("Cleaning tasks done... \nBean object destroyed!");
       
    }

   
}

Run the Project

Now, run the application again and check the console. The output should be similar as the previous output.

Initializing tasks done...

Bean Executing...

Name: Rohan

Cleaning tasks done...

Bean object destroyed!

About the author

Irfan Khan

I’m a software programmer having more than 5 years of development experience in Java and related technology. I love to write technical content as well and love to share the technical knowledge to make it available for all.