The Bean Lifecycle
The Bean Lifecycle
Every object managed by the Spring container goes through a well-defined lifecycle: it is instantiated, its dependencies are populated, initialisation callbacks run, the bean serves the application, and finally destruction callbacks clean up resources. Understanding each phase lets you hook into exactly the right moment — whether you need to open a connection pool, validate configuration, or flush a cache before shutdown.
The Four Phases at a Glance
- Instantiation — the container creates the raw object using the constructor (or a factory method).
- Dependency population — Spring injects all dependencies: constructor arguments, setter calls, and field injection via
@Autowired. - Initialisation — once all dependencies are in place, initialisation callbacks fire. Your code can now use fully-injected collaborators.
- Destruction — when the context is closed (application shutdown, test teardown, etc.), destruction callbacks release resources.
null because Spring has not had a chance to set it yet.
Phase 1 — Instantiation
Spring creates the bean instance using reflection. For a singleton bean (the default scope) this happens once when the application context starts. For a prototype bean it happens each time one is requested. At this point the object is a plain Java object — no dependencies have been set.
Constructor injection collapses Phases 1 and 2 into a single step, which is one reason it is preferred: the object is never visible in a partially-initialised state.
Phase 2 — Dependency Population
After instantiation (for setter/field injection), Spring inspects the bean definition and resolves each dependency from the container, then calls the appropriate setter or writes directly to the field. This is when @Autowired, @Value, and @Inject are processed.
Phase 3 — Initialisation
Once all dependencies are injected, Spring calls the initialisation callbacks in this order:
- Methods annotated with
@PostConstruct(recommended) - The
afterPropertiesSet()method if the bean implementsInitializingBean - A custom
init-methoddeclared in@Bean(initMethod = "...")
@PostConstruct is the cleanest option: it is a standard Java annotation (jakarta.annotation.PostConstruct) independent of Spring, so the class stays portable and testable.
@PostConstruct for: opening resources (thread pools, connection pools), loading configuration into memory, validating that required dependencies are correctly wired, or populating caches. Keep it fast — a slow @PostConstruct delays context startup.
Phase 4 — Destruction
When the Spring ApplicationContext is closed — on JVM shutdown, on a context.close() call in a test, or when a web application undeploys — destruction callbacks run for all singleton beans in reverse registration order:
- Methods annotated with
@PreDestroy(recommended) - The
destroy()method if the bean implementsDisposableBean - A custom
destroy-methoddeclared in@Bean(destroyMethod = "...")
@PreDestroy will never be called on a prototype-scoped bean. If your prototype bean holds resources, you must manage its lifecycle manually.
Using @Bean Init and Destroy Methods
When integrating a third-party class that you cannot annotate (because you do not own its source), use the initMethod and destroyMethod attributes on @Bean:
close() or shutdown() as the destroy method for beans returned by @Bean methods, so you often do not need to specify destroyMethod explicitly. You can suppress this by setting destroyMethod = "".
The Full Lifecycle — One Complete Example
The following class deliberately exercises all four phases so you can observe the ordering in your logs:
Expected log output on startup and context.close():
Summary
The Spring bean lifecycle has four distinct phases: instantiation (constructor), dependency population (injection), initialisation (@PostConstruct → afterPropertiesSet() → custom init), and destruction (@PreDestroy → destroy() → custom destroy). In practice, reach for @PostConstruct and @PreDestroy — they keep your beans portable and readable. Use @Bean(initMethod, destroyMethod) when integrating third-party classes. Remember that prototype beans opt out of Spring-managed destruction entirely.