Understanding the Full Spring Bean Lifecycle: From Instantiation to Destruction

This article walks through the complete Spring Bean lifecycle—covering scanning, instantiation, dependency injection, initialization, AOP proxy creation, and destruction—while highlighting common pitfalls, the differences for prototype and lazy‑loaded beans, and practical debugging tips.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Understanding the Full Spring Bean Lifecycle: From Instantiation to Destruction

Clarify Three Commonly Confused Basic Concepts

Many developers mix up the three fundamental phases of a Bean: Instantiation, Property Population, and Initialization. The following sections are organized around these phases.

Instantiation : Allocate heap memory and create an empty object via the constructor; all @Autowired fields are null at this point.

Property Population : Perform dependency injection by assigning beans from the container to the object's fields.

Initialization : After all dependencies are ready, execute custom pre‑business logic such as loading dictionaries, initializing thread pools, or registering callbacks.

In most projects, about 95% of beans are non‑lazy singleton beans that are created once during container startup and then reused globally.

1. Full Lifecycle of a Non‑Lazy Singleton Bean

The lifecycle consists of five major stages—Scanning → Instantiation → Property Population → Initialization → Destruction—refined into nine immutable execution nodes, each annotated with typical developer pitfalls.

1. Scanning and Parsing to Generate BeanDefinition

During startup, Spring scans configuration classes and package paths, discovers classes annotated with @Service, @Component, or @Repository, and records their metadata (class name, dependencies, scope, init/destroy methods) into a BeanDefinition stored in the registry. No objects are created at this point.

2. Instantiation – Creating an Empty Object

Spring iterates the BeanDefinition registry, uses reflection to invoke the class constructor (preferring a parameterized constructor, otherwise the default constructor), allocates heap space, and produces a raw object. Constructor‑injected beans receive their dependencies at this step, which explains why constructor injection avoids NullPointerException.

3. Pre‑ and Post‑Instantiation Processors

Spring calls InstantiationAwareBeanPostProcessor before the object is fully created, allowing replacement of the raw instance. A typical use case is MyBatis mapper interfaces: Spring generates a dynamic proxy at this node instead of instantiating the interface.

4. Dependency Injection (Property Population)

For field or setter injection, Spring resolves internal dependencies and assigns them from the first‑level or third‑level cache. Circular dependencies are also resolved here by exposing early object references via the third‑level cache. Constructor‑injected beans skip this step.

5. Post‑Processor After Property Modification

After property population, Spring invokes post‑processors that can modify injected properties. Common scenarios include @Value configuration injection, Nacos dynamic refresh, and parameter masking.

6. Pre‑Initialization Processing

All dependencies are now 100% ready, so NullPointerException cannot occur. Before invoking custom init methods, Spring runs BeanPostProcessor pre‑methods for shallow object modifications.

7. Ordered Execution of Initialization Methods

Aware interfaces (BeanNameAware, BeanFactoryAware) – rarely implemented manually.

@PostConstruct – most frequently used, simple and non‑intrusive, executes first.

InitializingBean – Spring native interface, high coupling, seldom used in new projects.

Custom init‑method – legacy XML configuration, now largely deprecated.

8. Post‑Initialization Processing (AOP Proxy Core)

After all initialization steps, Spring executes BeanPostProcessor post‑methods. This is where all AOP dynamic proxies are created. Because the proxy is generated last, internal method calls (this.xxx) bypass the proxy, causing aspect failure.

9. Container Shutdown and Destruction Logic

Destruction runs only on graceful shutdown (e.g., invoking container.close()). Forced termination (kill ‑9) skips destruction. The fixed destruction order includes:

@PreDestroy annotated method.

DisposableBean.destroy method.

Custom destroy‑method.

Typical uses: closing DB connection pools, stopping scheduled threads, clearing local caches.

2. Lifecycle Differences for Two Special Bean Types

2.1 Prototype Bean

Prototype beans differ from singleton beans in two ways, often leading to resource leaks:

Creation is delayed until the bean is first requested or injected, so the full 1‑8 steps run at that moment.

There is no destruction phase; Spring does not retain a reference, so step 9 never executes. If a prototype bean holds a file stream or DB connection, it relies on JVM GC, which can exhaust connection pools.

Real‑world incident: a file‑processing prototype bean left DB connections open for half a month, eventually exhausting the connection pool.

2.2 Lazy‑Loaded Singleton Bean (@Lazy)

The lifecycle steps are identical to a regular singleton; only the creation timing changes. The bean is instantiated on first use rather than during container startup, useful for breaking circular dependencies and reducing startup time.

3. Important Considerations

1️⃣ @PostConstruct cannot use injected dependencies – Correction: It runs after property population, so all dependencies are already injected and can be safely used.

2️⃣ kill ‑9 will trigger destroy methods – Correction: Forced termination bypasses all destruction logic; graceful shutdown must be used in production.

3️⃣ Circular dependencies scramble the lifecycle order – Correction: They only expose early object references; the nine-node order remains unchanged.

💬 Interaction

Share any Bean‑lifecycle bugs you’ve encountered, such as initialization NullPointerExceptions or aspect failures, in the comments.

Continuous sharing of easy‑to‑understand Spring internals, avoiding source‑code clutter and rote patterns. If you find this useful, feel free to like and bookmark for future reference.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AOPSpringDependency InjectionLazy InitializationBean LifecyclePrototype Bean
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.