Spring Core - Spring Bean Lifecycle
The Spring Bean Lifecycle defines the sequence of events that occur during the creation, initialization, and destruction of a bean managed by the Spring container. Understanding this lifecycle is essential for managing resources and ensuring proper initialization and cleanup of beans.
Lifecycle Phases
- Bean Instantiation
- The Spring container instantiates the bean by calling its constructor or a static factory method.
- Populate Properties (Dependency Injection)
- The container injects values into the bean's properties, typically through setter methods or constructor arguments.
- Bean Name and Factory Aware Callbacks (Optional)
- If the bean implements specific interfaces (e.g., BeanNameAware, BeanFactoryAware), the container will call the corresponding methods to provide context (e.g., bean name or reference to the BeanFactory).
- Bean Initialization
- Pre-initialization: The container applies BeanPostProcessors before the initialization method.
- Custom Initialization: If specified, the initialization logic is executed (e.g., methods annotated with @PostConstruct or declared via init-method).
- Post-initialization: The container applies BeanPostProcessors after the initialization method.
- Bean Ready for Use
- The bean is now fully initialized and ready to be used within the application.
- Bean Destruction
- Before the container destroys a bean (e.g., during shutdown), it executes any destruction logic (e.g., methods annotated with @PreDestroy or declared via destroy-method).
Diagram of the Spring Bean Lifecycle
1. Instantiate Bean
↓
2. Dependency Injection (populate properties)
↓
3. Aware Interfaces (if implemented)
↓
4. BeanPostProcessor (Before Initialization)
↓
5. Initialization (e.g., @PostConstruct, init-method)
↓
6. BeanPostProcessor (After Initialization)
↓
7. Bean Ready for Use
↓
8. Destruction (e.g., @PreDestroy, destroy-method)
Lifecycle Methods and Hooks
1. Using @PostConstruct and @PreDestroy
Annotate methods for initialization and cleanup tasks.
@Component
public class MyBean {
@PostConstruct
public void init() {
System.out.println("Bean is initialized!");
}
@PreDestroy
public void destroy() {
System.out.println("Bean is being destroyed!");
}
}
2. Implementing Lifecycle Interfaces
- InitializingBean: Define custom initialization logic in the afterPropertiesSet() method.
- DisposableBean: Define custom destruction logic in the destroy() method.
@Component
public class MyBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() {
System.out.println("Custom initialization logic in afterPropertiesSet()");
}
@Override
public void destroy() {
System.out.println("Custom cleanup logic in destroy()");
}
}
3. Using @Bean with initMethod and destroyMethod
Lifecycle methods can be directly defined in a @Configuration class.
@Configuration
public class AppConfig {
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public MyBean myBean() {
return new MyBean();
}
}
public class MyBean {
public void customInit() {
System.out.println("Custom initialization logic");
}
public void customDestroy() {
System.out.println("Custom destruction logic");
}
}
4. Using BeanPostProcessor
Bean creation can be intercepted for applying custom logic before or after initialization.
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("Before Initialization: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("After Initialization: " + beanName);
return bean;
}
}
Best Practices
- Use @PostConstruct and @PreDestroy for simple lifecycle management.
- Prefer initMethod and destroyMethod for reusable beans.
- Use BeanPostProcessor sparingly for cross-cutting concerns.
- Avoid heavy initialization in constructors - use lifecycle methods instead.
- Always release resources during destruction to avoid memory leaks.
