Spring - Bean Lifecycle
Every Spring bean goes through a well-defined lifecycle: instantiation, dependency injection, initialisation, active use, and destruction. Spring provides hooks at the initialisation and destruction phases so you can run custom logic, for example, opening a connection pool on startup or releasing resources on shutdown.
Contents
Bean Lifecycle Phases
- Instantiation: Spring creates the bean instance.
- Property Population: dependencies are injected.
- Initialisation:
methods are called.@PostConstruct - Active use: the bean serves the application.
- Destruction:
methods are called when the context is closed.@PreDestroy
@PostConstruct: initialisation hook
A method annotated with @PostConstruct
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CacheWarmupService {
private final ProductRepository productRepository;
private List<Product> cachedProducts;
public CacheWarmupService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@PostConstruct
public void warmUpCache() {
cachedProducts = productRepository.findAll();
System.out.println("Cache warmed up with " + cachedProducts.size() + " products");
}
}
Rules: the method must be voidstatic
@PreDestroy: destruction hook
A method annotated with @PreDestroy
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Service
public class AsyncTaskService {
private final ExecutorService executor = Executors.newFixedThreadPool(4);
public void submitTask(Runnable task) {
executor.submit(task);
}
@PreDestroy
public void shutdown() throws InterruptedException {
System.out.println("Shutting down thread pool...");
executor.shutdown();
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
System.out.println("Thread pool terminated.");
}
}
InitializingBean and DisposableBean interfaces
An older approach is implementing InitializingBeanDisposableBean
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class DatabaseConnectionPool implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Connection pool initialised");
}
@Override
public void destroy() throws Exception {
System.out.println("Connection pool closed");
}
}
Prefer @PostConstruct@PreDestroy