Spring Bean Lifecycle: Creation, Initialization, and Destruction with Annotations

In Spring, the lifecycle of a bean is crucial for managing resources effectively and ensuring your application behaves as expected. From instantiation to destruction, Spring provides hooks to perform initialization and cleanup tasks for your beans. Understanding the lifecycle helps you manage the lifecycle of beans and resources efficiently.

1. Spring Bean Lifecycle Overview

The lifecycle of a Spring bean consists of the following stages:

  1. Instantiation: Spring creates the bean instance (using the default constructor or a specified constructor).
  2. Population of properties: Dependencies are injected into the bean (via constructor, setter, or field injection).
  3. Initialization: Any initialization logic (e.g., custom methods or annotations) is executed.
  4. Destruction: The bean is destroyed when the context is closed or the bean is no longer required.

2. Bean Creation Process

Let’s break down the main stages of the Spring Bean lifecycle.

a. Instantiation

The first step is the instantiation of the bean. This occurs when the Spring container creates the bean instance, either by calling a constructor or by using factory methods.

@Component
public class Car {
    private Engine engine;

    // Constructor used for dependency injection
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.start();
        System.out.println("Car is running...");
    }
}

b. Population of Properties

Once the bean is instantiated, Spring injects the dependencies (if required). Spring uses constructor injection, setter injection, or field injection to populate the properties.

@Component
public class Engine {
    public void start() {
        System.out.println("Engine started.");
    }
}

c. Initialization

After the bean is instantiated and properties are injected, Spring proceeds with initialization. You can define custom initialization logic by using annotations like @PostConstruct or by specifying initialization methods in the bean definition.

d. Destruction

When the bean is no longer needed, or the application context is closed, Spring handles the destruction of the bean. You can define cleanup logic using annotations like @PreDestroy or specify destruction methods.

3. Using @PostConstruct and @PreDestroy Annotations

Spring provides two useful annotations to manage the lifecycle of a bean:

  • @PostConstruct: This annotation is used to define a method that should be called after the bean has been initialized (i.e., after dependencies are injected).
  • @PreDestroy: This annotation is used to define a method that should be called before the bean is destroyed.

Example with @PostConstruct and @PreDestroy

@Component
public class Car {
    private Engine engine;

    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }

    // @PostConstruct is called after the bean is initialized
    @PostConstruct
    public void init() {
        System.out.println("Car bean has been initialized.");
    }

    // @PreDestroy is called before the bean is destroyed
    @PreDestroy
    public void cleanup() {
        System.out.println("Car bean is about to be destroyed.");
    }

    public void drive() {
        engine.start();
        System.out.println("Car is running...");
    }
}

In the above example, the init() method annotated with @PostConstruct is invoked after the Car bean is initialized, and the cleanup() method annotated with @PreDestroy is invoked when the Car bean is destroyed.

4. Configuring Bean Initialization and Destruction Methods

In addition to using annotations, Spring also allows you to configure custom initialization and destruction methods in XML or Java configuration files.

a. XML-based Configuration

In XML configuration, you can specify init-method and destroy-method attributes for your beans.

<bean id="car" class="com.example.Car" init-method="init" destroy-method="cleanup">
    <constructor-arg ref="engine"/>
</bean>

In the above example, the init method is called after the bean is initialized, and the cleanup method is called before the bean is destroyed.

b. Java-based Configuration

If you are using Java-based configuration with annotations, you can define the @Bean methods in a @Configuration class to specify the init and destroy methods.

@Configuration
public class AppConfig {

    @Bean(initMethod = "init", destroyMethod = "cleanup")
    public Car car() {
        return new Car(new Engine());
    }
}

In this configuration, the init and cleanup methods are called automatically when the bean is initialized and destroyed.

 

Spring Bean Lifecycle Example

Here’s a complete example of a Spring application that demonstrates the bean lifecycle with initialization and destruction logic.

Step 1: Create Beans

Engine.java

@Component
public class Engine {
    public void start() {
        System.out.println("Engine started.");
    }
}

Car.java

@Component
public class Car {
    private Engine engine;

    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }

    @PostConstruct
    public void init() {
        System.out.println("Car bean has been initialized.");
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("Car bean is about to be destroyed.");
    }

    public void drive() {
        engine.start();
        System.out.println("Car is running...");
    }
}

Step 2: Configure Spring

AppConfig.java

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

Step 3: Main Application

MainApp.java

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Car car = context.getBean(Car.class);
        car.drive();

        // Close context to trigger bean destruction
        ((AnnotationConfigApplicationContext) context).close();
    }
}

Step 4: Run the Application

When you run the MainApp class, the following output will be generated:

Car bean has been initialized.
Engine started.
Car is running...
Car bean is about to be destroyed.

Best Practices for Bean Lifecycle Management

  1. Use @PostConstruct for initialization: It’s best to use @PostConstruct for any setup or resource allocation that needs to occur after the bean is instantiated.
  2. Use @PreDestroy for cleanup: Similarly, @PreDestroy is ideal for cleanup operations such as closing connections or releasing resources.
  3. Avoid long-running operations: The lifecycle methods (init, cleanup) should be short and lightweight to ensure that bean management remains efficient.
  4. Custom Initialization and Destruction: If you need more control over bean lifecycle management, use init-method and destroy-method attributes in XML or Java configuration.