Foundation · Understand

The Bean Lifecycle

A bean is instantiated, populated, post-processed, initialized, used, and destroyed—in that order, with a hook at each seam.

Primary source: Spring Framework 6.2 Reference: Customizing the Nature of a Bean

25 min

Lesson 10 of 14

1d streak

The Architectural Question

Constructor bodies run before injection completes, so touching late-arriving dependencies there reads null and the failure appears only for specific wiring styles.

What this guide proves

  • @PostConstruct
  • @PreDestroy
  • InitializingBean

The complete 10-step bean lifecycle timeline

From the moment Spring decides to instantiate a bean until its eventual disposal, AbstractAutowireCapableBeanFactory moves the object through a strict 10-step lifecycle:

StepLifecycle StageMechanism / InterfaceArchitectural Purpose
1InstantiationConstructor reflection or CGLIBAllocates raw heap memory for the object instance.
2Populate PropertiesDependency InjectionResolves and injects constructor/setter collaborators.
3Aware CallbacksBeanNameAware, BeanFactoryAwareSupplies container infrastructure references if requested.
4Pre-Initialization BPPBeanPostProcessor.postProcessBeforeInitializationExecutes custom setup; CommonAnnotationBeanPostProcessor detects @PostConstruct.
5@PostConstruct@PostConstruct annotationPrimary initialisation hook for application code after dependencies are wired.
6InitializingBeanInitializingBean.afterPropertiesSet()Spring-specific programmatic initialization contract.
7Custom init-method@Bean(initMethod = "...")Declarative configuration initialization hook.
8Post-Initialization BPPBeanPostProcessor.postProcessAfterInitializationGenerates dynamic AOP proxies (@Transactional, @Cacheable, @Async).
9Ready for UseApplication trafficBean is cached in singleton registry and serves requests.
10Destruction@PreDestroy, DisposableBean.destroy()Executes teardown logic and resource release on context shutdown.

Verify the lifecycle with an executable probe

We can observe every lifecycle stage in sequence by creating a diagnostic probe component:

LifecycleAuditProbe.java
@Component
public class LifecycleAuditProbe implements BeanNameAware, InitializingBean, DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(LifecycleAuditProbe.class);

    public LifecycleAuditProbe() {
        log.info("1. Constructor executed (Instantiation)");
    }

    @Override
    public void setBeanName(String name) {
        log.info("2. BeanNameAware executed with name: {}", name);
    }

    @PostConstruct
    public void postConstruct() {
        log.info("3. @PostConstruct executed (Dependencies populated)");
    }

    @Override
    public void afterPropertiesSet() {
        log.info("4. InitializingBean.afterPropertiesSet() executed");
    }

    @PreDestroy
    public void preDestroy() {
        log.info("5. @PreDestroy executed (Container shutting down)");
    }

    @Override
    public void destroy() {
        log.info("6. DisposableBean.destroy() executed");
    }
}

Break it deliberately: Premature collaborator invocation in constructor

Invoking business operations or accessing proxy-wrapped features inside a class constructor fails because the bean lifecycle has only reached Step 1: dependencies may not be wired, and AOP proxies do not exist yet.

PrematureCallDemo.java
@Component
public class CacheWarmupService {
    @Autowired
    private RemoteDatabase database; // Field injection

    public CacheWarmupService() {
        // Step 1: Constructor runs BEFORE properties are populated!
        database.loadAllData(); // Throws NullPointerException!
    }
}

The Golden Rule

Never execute business logic or external network calls inside a constructor. Constructors should only assign fields. Move all startup initialization logic into an @PostConstruct method or an ApplicationReadyEvent listener.

Check your understanding

Active Recall Checkpoint

At what exact step in the bean lifecycle are dynamic AOP proxies (@Transactional, @Cacheable) created?

Hint: Before or after initialization?

Show answer & explanation

Dynamic AOP proxies are created in Step 8 during BeanPostProcessor.postProcessAfterInitialization(), after all properties are populated and @PostConstruct has finished.

Active Recall Checkpoint

What is the execution order between @PostConstruct, InitializingBean.afterPropertiesSet(), and a custom @Bean(initMethod)?

Hint: Annotation first, interface second, declarative last.

Show answer & explanation

The order is strictly: (1) @PostConstruct, (2) InitializingBean.afterPropertiesSet(), and (3) Custom initMethod.

Runtime Architecture & Flow

Spring container lifecycle

Construction is only the first lifecycle phase

Follow one singleton from its bean definition to the reference that application code actually receives, then through shutdown.

ApplicationContext refresh
ApplicationContext close

Phase 1 of 6

Spring reads the bean definition

Metadata tells the container which type to create, its scope, dependencies, and lifecycle callbacks.

Interactive Spring Runtime Lab

Spring Bean Lifecycle & CGLIB Proxy Simulator

Phase 1/9: 1. BeanDefinition Registration
Active ProcessMetadata

1. BeanDefinition Registration

ClassPathBeanDefinitionScanner registers AnnotatedGenericBeanDefinition with metadata (scope=singleton, lazyInit=false).

JVM Heap Inspection
Memory AddressN/A (Unallocated)
Runtime ClassBeanDefinition (Metadata)
Collaborators WiredNO (null)
CGLIB Proxy BuiltNO
Spring Internal ConsoleLIVE STREAM

2026-10-19 12:00:00.124 [main] INFO org.springframework.boot.Startup:

> DEBUG o.s.b.f.s.DefaultListableBeanFactory: Creating shared instance of singleton bean 'orderService'

Thread: main [PID: 45018]DefaultListableBeanFactory

Working Implementation

Code Walkthrough

Use `@PostConstruct` to validate configuration or load initial cache data immediately after dependency injection completes.

BeanLifecycleTimelineLab.java
package com.example.core;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class BeanLifecycleTimelineLab implements BeanNameAware, InitializingBean {
    private String beanName;

    // Step 1: JVM Constructor
    public BeanLifecycleTimelineLab() {
        System.out.println("[Lifecycle 1] Constructor: Memory allocated, properties not yet injected");
    }

    // Step 3: Aware callbacks
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("[Lifecycle 3] BeanNameAware: Injected bean name = " + beanName);
    }

    // Step 5: JSR-250 @PostConstruct
    @PostConstruct
    public void initPostConstruct() {
        System.out.println("[Lifecycle 5] @PostConstruct: Dependencies populated, validating connections");
    }

    // Step 6: InitializingBean.afterPropertiesSet
    @Override
    public void afterPropertiesSet() {
        System.out.println("[Lifecycle 6] InitializingBean: Framework initialization complete");
    }

    // Step 8: Destruction callback
    @PreDestroy
    public void cleanup() {
        System.out.println("[Lifecycle 8] @PreDestroy: Closing connections and flushing buffers");
    }
}
Constructors allocate memory; properties and collaborators are only available in @PostConstruct and afterPropertiesSet.
Touching dependencies in constructors risks NullPointerException with late-arriving or proxy-wrapped beans.
@PreDestroy executes during context shutdown, enabling graceful release of sockets, files, and thread pools.

Synthesis & Production Verification

Production Acceptance Criteria

  • Sequence constructor, @PostConstruct, and afterPropertiesSet from log timestamps
  • Load cache data in @PostConstruct without touching unwired fields
  • Release resources in @PreDestroy and observe it during shutdown

Architectural & Technical Review

Core Interview Questions

Q01At what exact point in the 10-step bean lifecycle are dynamic AOP proxies (@Transactional, @Cacheable) generated?Show answer

Dynamic AOP proxies are created in Step 8 during BeanPostProcessor.postProcessAfterInitialization(), after all properties are populated and @PostConstruct has finished.

Q02What is the strict execution order between @PostConstruct, InitializingBean.afterPropertiesSet(), and custom initMethod?Show answer

The execution order is strictly: (1) @PostConstruct, (2) InitializingBean.afterPropertiesSet(), and (3) Custom initMethod.

Q03Why should heavy business logic or remote network calls never be placed in a class constructor?Show answer

Constructors run before dependencies are populated and before AOP proxies or security interceptors attach. Premature invocation causes NullPointerExceptions or transaction bypasses.

Production Pitfalls

Critical Anti-Patterns

  • Pitfall: Executing database or network operations inside constructors — Constructors should only assign fields. Move startup initialization logic into @PostConstruct or ApplicationReadyEvent listeners.
  • Pitfall: Relying on @Transactional behavior inside @PostConstruct — @PostConstruct runs before postProcessAfterInitialization, meaning dynamic AOP transaction proxies have not yet attached.

Primary Documentation & Specifications

Supplementary Module Resources