Unravel โบ Java concepts โบ Spring and Boot
Bean lifecycle and startup hooks
Spring runs code at fixed moments in a bean life: right after it is built and wired, when the application is ready, and just before it is thrown away.
Learn these first
At a glance
A restaurant day: before opening you lay the tables (post-construct), a bell rings when the doors open (application ready), and at closing you count the till and switch off the stoves (pre-destroy).
- 1The order is: constructor, dependency injection, @PostConstruct (or afterPropertiesSet), then the bean is ready for useOn shutdown, @PreDestroy (or destroy) runs.
- 2CommandLineRunner and ApplicationRunner beans run once after the context starts, with the command-line argumentsThey are the usual place for one-off start tasks such as warming a cache or seeding data.
- 3@EventListener(ApplicationReadyEvent.class) runs when the app is fully started and the web server accepts traffic, which is a better moment than @PostConstruct for calling other services
- 4SmartLifecycle beans have start() and stop() with a phase number, so components start and stop in a controlled order (a message consumer starts last and stops first)
- Slow or failing work inside @PostConstruct delays or aborts startup, and Kubernetes may restart the pod before it ever becomes healthy
- Do not call @Transactional or @Async methods of the same bean from @PostConstruct: the proxy is not in place yet, so the annotations do nothing
- Bean post-processors wrap each creation: postProcessBeforeInitialization (where @PostConstruct is applied), then afterPropertiesSet, then postProcessAfterInitialization (where proxies are created)
@Componentpublic class CacheWarmer implements ApplicationRunner { private final ProductService products; public CacheWarmer(ProductService products) { this.products = products; } @Override public void run(ApplicationArguments args) { products.loadTopSellers(); } @PreDestroy void close() { products.flushStats(); }}- line 2 uses ApplicationRunner, the core of Bean lifecycle and startup hooks
- line 11 uses PreDestroy, the core of Bean lifecycle and startup hooks
Read the full explanation (about 2 min)
Spring runs code at fixed moments in a bean life: right after it is built and wired, when the application is ready, and just before it is thrown away. Those hooks are where startup and shutdown work belongs.
A mental model
A restaurant day: before opening you lay the tables (post-construct), a bell rings when the doors open (application ready), and at closing you count the till and switch off the stoves (pre-destroy).
How it works
The order is: constructor, dependency injection, @PostConstruct (or afterPropertiesSet), then the bean is ready for use. On shutdown, @PreDestroy (or destroy) runs.
CommandLineRunner and ApplicationRunner beans run once after the context starts, with the command-line arguments. They are the usual place for one-off start tasks such as warming a cache or seeding data.
@EventListener(ApplicationReadyEvent.class) runs when the app is fully started and the web server accepts traffic, which is a better moment than @PostConstruct for calling other services.
SmartLifecycle beans have start() and stop() with a phase number, so components start and stop in a controlled order (a message consumer starts last and stops first).
Going deeper
Slow or failing work inside @PostConstruct delays or aborts startup, and Kubernetes may restart the pod before it ever becomes healthy. Keep it short, and move long work to a background thread or a ready-event.
Do not call @Transactional or @Async methods of the same bean from @PostConstruct: the proxy is not in place yet, so the annotations do nothing.
A graceful shutdown (server.shutdown=graceful) lets in-flight requests finish before @PreDestroy hooks release pools and close connections.
Under the hood
Bean post-processors wrap each creation: postProcessBeforeInitialization (where @PostConstruct is applied), then afterPropertiesSet, then postProcessAfterInitialization (where proxies are created). That is why proxy features are not active inside @PostConstruct.
Common mistakes
- Heavy blocking work in
@PostConstruct. - Relying on
@Transactionalinside@PostConstruct. - Using prototype-scoped beans and expecting
@PreDestroyto run for them (Spring does not manage their destruction). - Starting threads in a bean and never stopping them on shutdown.
How Unravel recognises it
When you paste code, Unravel looks for these signs of bean lifecycle and startup hooks. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@PostConstruct@PreDestroy@Startup - Types:
InitializingBeanDisposableBeanSmartLifecycleCommandLineRunnerApplicationRunnerApplicationReadyEventContextRefreshedEventContextClosedEventApplicationStartedEventSmartInitializingSingletonApplicationContextAwareBeanNameAware
Part of a learning path
- Inside Spring Boot ยท previous: @Configuration, @Bean and bean wiring ยท next: Writing aspects (@Aspect, pointcuts, advice)
Related concepts
Frequently asked questions
What is Bean lifecycle and startup hooks?
Spring runs code at fixed moments in a bean life: right after it is built and wired, when the application is ready, and just before it is thrown away. Those hooks are where startup and shutdown work belongs.
What are the most common mistakes with Bean lifecycle and startup hooks?
Heavy blocking work in @PostConstruct. Relying on @Transactional inside @PostConstruct. Using prototype-scoped beans and expecting @PreDestroy to run for them (Spring does not manage their destruction). Starting threads in a bean and never stopping them on shutdown.
How can I see Bean lifecycle and startup hooks in real code?
Paste your own Java into Unravel and it will point out where bean lifecycle and startup hooks appears, explain how it works at the detail level you choose, and draw the flow of the code around it.