Spring does a surprising amount of work between "I found a @Component" and "the bean is ready to inject" — and it's all invisible. So I built a visualizer that animates a real ApplicationContext starting up, one phase at a time.
▶ Live demo: https://dev48v.github.io/bean-lifecycle/
Source (single file, zero deps): https://github.com/dev48v/bean-lifecycle
Every phase a singleton goes through
Press "refresh context" and each bean walks the full lifecycle, the way real Spring does it:
- instantiate — constructor runs
-
populate —
@Autowireddependencies injected -
aware —
*Awarecallbacks (BeanNameAware,ApplicationContextAware, …) -
BeanPostProcessor — before (
postProcessBeforeInitialization) -
@PostConstruct— your init method -
afterPropertiesSet—InitializingBean/ custominit-method -
BeanPostProcessor — after (
postProcessAfterInitialization— where AOP proxies are created) - ready — placed in the singleton cache
That step 7 is the one most people miss: AOP proxies are created by a BeanPostProcessor after initialization. It's why @Transactional/@Async work — the bean you actually get injected is a proxy wrapping your instance.
The two things the animation makes obvious
Creation follows dependencies. Beans are built bottom-up:
appConfig → dataSource → orderRepository → orderService → orderController
A bean can't be injected until the bean it needs exists, so Spring topologically orders creation. On context.close(), it destroys in reverse order (@PreDestroy → DisposableBean.destroy()) so nothing is torn down while something still depends on it.
Prototype beans are different — and dangerous. Request a prototype bean and Spring builds it fresh on demand, runs init... and then forgets about it. It never calls @PreDestroy on a prototype. Spring hands you the instance and washes its hands. If that bean holds a file handle or connection, you are responsible for closing it — a classic, quiet memory leak.
Why a visualizer
You can read "BeanPostProcessors run around initialization" and still be fuzzy on where exactly @PostConstruct sits relative to the proxy creation. Watching the phases light up in order — and watching the prototype skip its destroy callback — turns a list of terms into a model you can reason with.
One index.html, no build. If it helped, a star helps others find it: https://github.com/dev48v/bean-lifecycle