Lifecycle Hooks
Lifecycle Hooks
Spring manages every bean from the moment it is instantiated to the moment the application context shuts down. Along that journey the container provides two well-defined hook points where your code can run: right after construction and dependency injection are complete (post-construct), and just before the bean is removed from the context (pre-destroy). Knowing how to use these hooks correctly — and which API to choose — is essential for writing beans that start reliably and clean up after themselves.
Why Lifecycle Hooks Matter
Consider a bean that wraps a database connection pool, or one that opens a file handle, or that must register itself with an external service at startup. None of that work belongs in the constructor — at that point Spring has not yet injected the bean's dependencies. The constructor must remain lightweight and side-effect-free. @PostConstruct gives you a guaranteed safe moment to run initialisation after all dependencies are fully wired.
The symmetric need exists at shutdown: connection pools must be drained, sockets must be closed, and external registrations must be revoked. @PreDestroy gives you a predictable moment to do all of that before the JVM exits.
@PostConstruct — Initialise After Wiring
@PostConstruct is a Jakarta EE annotation (package jakarta.annotation) supported by Spring out of the box. You place it on any public, package-private, or protected method that takes no arguments and returns void. Spring calls that method once, immediately after it has finished injecting all dependencies.
outputDir inside the constructor it would be null — Spring injects field values after the constructor returns. @PostConstruct is the earliest safe moment to use any injected value.
@PreDestroy — Clean Up Before Shutdown
@PreDestroy, from the same jakarta.annotation package, marks a method Spring will call just before destroying the bean — typically during ApplicationContext.close() or on JVM shutdown if the context registered a shutdown hook. The same signature rules apply: no arguments, void return type.
InitializingBean and DisposableBean — The Interface Approach
Before @PostConstruct and @PreDestroy became mainstream, Spring provided the same hook points through two interfaces in org.springframework.beans.factory:
InitializingBean— declaresafterPropertiesSet(), called after injection completes.DisposableBean— declaresdestroy(), called before bean destruction.
Annotation vs Interface: Which Should You Choose?
The two mechanisms are functionally equivalent when used on singleton beans. The practical difference is one of coupling:
- Prefer
@PostConstruct/@PreDestroyin almost every new project. These are standard Jakarta EE annotations — your bean compiles and works even without Spring on the classpath, which makes it easier to unit-test in isolation. - Use the interfaces only when you are writing framework-level infrastructure code that deliberately targets the Spring API, or when you are maintaining legacy code that already uses them.
default-init-method) and per-bean init-method / destroy-method attributes in @Bean definitions. For a @Bean method you can write @Bean(initMethod = "start", destroyMethod = "stop") — useful when you do not own the class and cannot add annotations to it (for example, a third-party library).
The Exact Execution Order
When all three mechanisms are present on the same bean, Spring calls them in a defined sequence:
- Constructor
- Dependency injection (fields, setters)
@PostConstructmethodInitializingBean.afterPropertiesSet()@Bean(initMethod = ...)method
On shutdown the reverse order applies for destroy callbacks:
@PreDestroymethodDisposableBean.destroy()@Bean(destroyMethod = ...)method
In practice you rarely mix mechanisms on one bean, but understanding the order helps when you inherit a base class that already implements InitializingBean and you want to add a @PostConstruct override in a subclass.
Registering a Shutdown Hook
For standalone Spring applications (not running in a servlet container), you must explicitly register a JVM shutdown hook so that @PreDestroy callbacks fire when the process exits. The simplest way is to use ConfigurableApplicationContext:
Spring Boot does this automatically. In a plain Spring context you are responsible for calling registerShutdownHook() or calling ctx.close() explicitly.
Practical Pattern: Warming Up a Cache
A classic @PostConstruct use case is pre-loading data so the first real request does not pay a cold-start penalty:
Summary
@PostConstruct and @PreDestroy are the standard, portable lifecycle hooks for Spring beans. They give you safe, well-timed access to run initialisation after injection and cleanup before destruction. The InitializingBean / DisposableBean interfaces predate the annotations and are still valid but couple your bean to the Spring API. For third-party classes you cannot annotate, use the initMethod / destroyMethod attributes of @Bean. Always register a shutdown hook in standalone apps. In the next lesson you will see how lazy initialisation lets you defer this entire startup sequence until a bean is actually needed.