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:
- Instantiation: Spring creates the bean instance (using the default constructor or a specified constructor).
- Population of properties: Dependencies are injected into the bean (via constructor, setter, or field injection).
- Initialization: Any initialization logic (e.g., custom methods or annotations) is executed.
- 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
- 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. - Use
@PreDestroy
for cleanup: Similarly,@PreDestroy
is ideal for cleanup operations such as closing connections or releasing resources. - Avoid long-running operations: The lifecycle methods (
init
,cleanup
) should be short and lightweight to ensure that bean management remains efficient. - Custom Initialization and Destruction: If you need more control over bean lifecycle management, use
init-method
anddestroy-method
attributes in XML or Java configuration.