DevNotes
Support
Spring Boot · Chapter 20

Bean Lifecycle

From BeanDefinition to destruction — Aware interfaces, BeanPostProcessor, @PostConstruct, InitializingBean, init/destroy methods, @PreDestroy, bean scopes and BeanFactoryPostProcessor.

◷ 8 min read§ 22 sections? 10 interview questions{ } 17 code examples

#Introduction

One of the most important Spring interview topics. Almost every senior-level interviewer asks some variation of: Explain the Spring bean lifecycle. When is @PostConstruct called? When is @PreDestroy called? What's the difference between BeanFactoryPostProcessor and BeanPostProcessor? What are bean scopes? What is a singleton bean? How are prototype beans destroyed? What happens internally when Spring starts?

Understanding the bean lifecycle explains how Spring creates, initializes, manages, proxies and destroys every bean.

#What is a Spring Bean?

A bean is simply an object that is created by Spring, managed by Spring and stored inside the IoC container.

Java
@Service
public class UserService {
}

#Complete Bean Lifecycle

Application starts → @ComponentScan
BeanDefinition created
Bean instantiation
Dependency injection
Aware interfaces
BeanPostProcessor — before initialization
@PostConstruct
afterPropertiesSet()
Custom initMethod()
BeanPostProcessor — after initialization
Bean ready for use
Application running
Application shutdown
@PreDestroy
DisposableBean.destroy()
Custom destroyMethod()
Bean removed
This entire lifecycle is a favourite interview topic

#Step 1 — BeanDefinition Creation

Spring scans your project for @Component, @Service, @Repository, @Controller and @Configuration classes. Each discovered bean becomes a BeanDefinition, which stores metadata such as the bean class, name, scope, lazy/eager initialization, constructor information and lifecycle callbacks. No object has been created yet.

#Step 2 — Bean Instantiation

Spring creates the object — internally like UserService service = new UserService();. The object exists, but dependencies aren't injected yet (except constructor arguments).

#Step 3 — Dependency Injection

Java
@Autowired
private UserRepository repository;
Instantiate bean
Resolve dependencies
Inject repository
Now the bean has all its required dependencies

#Step 4 — Aware Interfaces

If a bean implements an Aware interface, Spring injects framework objects:

Java
public class UserService implements BeanNameAware {

    @Override
    public void setBeanName(String name) {
        System.out.println(name);
    }
}

Common Aware interfaces: BeanNameAware, BeanFactoryAware, ApplicationContextAware, EnvironmentAware and ResourceLoaderAware. These are mainly used by framework developers and advanced infrastructure components.

#Step 5 — BeanPostProcessor (Before Initialization)

Spring calls postProcessBeforeInitialization():

Java
@Component
public class MyProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
}

This runs before the bean's init callbacks.

#Step 6 — @PostConstruct

One of the most asked interview questions.

Java
@Service
public class UserService {

    @PostConstruct
    public void init() {
        System.out.println("Bean Initialized");
    }
}

Called once, after bean creation and dependency injection. Perfect for loading caches, reading configuration, opening connections (if appropriate) and initializing in-memory data.

Create bean
Inject dependencies
@PostConstruct
Bean ready

#Step 7 — InitializingBean

Java
public class UserService implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
    }
}

afterPropertiesSet() runs after @PostConstruct. Today @PostConstruct is generally preferred because it avoids coupling your class to Spring interfaces.

#Step 8 — Custom initMethod

Java
@Bean(initMethod = "initialize")
public UserService service() {
    return new UserService();
}

public void initialize() {
}

Spring calls it after afterPropertiesSet().

Initialization Order

Interview favourite:

Constructor
Dependency injection
Aware methods
BeanPostProcessor (before initialization)
@PostConstruct
afterPropertiesSet()
Custom initMethod()
BeanPostProcessor (after initialization)

#Step 9 — BeanPostProcessor (After Initialization)

Spring executes postProcessAfterInitialization(). This is where many Spring features create proxies — Spring AOP, @Transactional, Spring Security, @Async and @Cacheable.

Why BeanPostProcessor Matters

Java
@Transactional
public void save() {
}
Original bean
Proxy bean
Transaction logic
Original method

Without BeanPostProcessor, features like @Transactional and @Async wouldn't work.

#Step 10 — Bean Ready

The bean is fully initialized and lives in the ApplicationContext, available to controllers, services and repositories.

#Step 11 — @PreDestroy

When the application stops:

Java
@Service
public class UserService {

    @PreDestroy
    public void destroy() {
        System.out.println("Cleaning Resources");
    }
}

Called before bean destruction. Useful for closing resources, releasing locks, flushing buffers and stopping background tasks.

#Step 12 — DisposableBean

Java
public class UserService implements DisposableBean {

    @Override
    public void destroy() {
    }
}

Again, @PreDestroy is generally preferred because it avoids Spring-specific coupling.

#Step 13 — Custom destroyMethod

Java
@Bean(destroyMethod = "cleanup")
public UserService service() {
    return new UserService();
}

Spring executes cleanup() during shutdown.

Destruction Order

Application shutdown
@PreDestroy
DisposableBean.destroy()
Custom destroyMethod()

#Bean Scopes

Interview favourite.

Singleton (Default)

Java
@Service
public class UserService {
}

One bean for the entire ApplicationContext, shared everywhere. The default scope.

Prototype

Java
@Scope("prototype")
@Component
public class Report {
}

Every request for the bean creates a new instance.

Request Scope

Java
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)

One bean per HTTP request. Only available in web applications. (@RequestScope is a shortcut that includes the proxy.)

Session Scope

Java
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)

One bean per HTTP session. Useful for user-specific state. (@SessionScope is the shortcut.)

Application Scope

Java
@Scope(value = "application")

One bean for the entire web application (ServletContext).

Scope Comparison

ScopeInstances
SingletonOne per ApplicationContext
PrototypeNew instance per lookup
RequestOne per HTTP request
SessionOne per HTTP session
ApplicationOne per web application

#BeanFactoryPostProcessor vs BeanPostProcessor

A very important interview topic.

BeanFactoryPostProcessor works on BeanDefinitions, before beans are created — e.g. modifying bean definitions, changing bean properties, registering additional bean definitions (PropertySourcesPlaceholderConfigurer resolves ${...} placeholders this way).

BeanPostProcessor works on actual bean instances:

Bean created
BeanPostProcessor
Modify / wrap bean
Return bean

…e.g. AOP proxies, transaction proxies, security proxies and logging wrappers.

BeanFactoryPostProcessorBeanPostProcessor
Works on BeanDefinitionWorks on the bean instance
Before instantiationAfter instantiation (around initialization)
MetadataObject
Runs earlierRuns later

This comparison is frequently asked.

#Complete Internal Flow

SpringApplication.run()
ApplicationContext
Component scan
BeanDefinition
BeanFactoryPostProcessor
Instantiate bean
Dependency injection
Aware interfaces
BeanPostProcessor (before)
@PostConstruct
afterPropertiesSet()
initMethod()
BeanPostProcessor (after) — AOP / transaction proxies
Application ready
Shutdown → @PreDestroy → destroy() → destroyMethod()

#Common Mistakes

Heavy Work in the Constructor

Java
// ❌ At construction time, field/setter dependencies aren't available yet
public UserService() {
    loadLargeCache();
}

// ✅ Better
@PostConstruct
public void init() {
    loadLargeCache();
}

Forgetting the Prototype Lifecycle

Developers often assume @PreDestroy runs for prototype beans. It doesn't — the application is responsible for cleaning them up if necessary.

Using BeanPostProcessor for Business Logic

BeanPostProcessor is for framework-level customization. Don't implement business rules there.

Depending on Bean Initialization Order

Don't assume one bean initializes before another unless you define that relationship explicitly (through dependency injection, or @DependsOn where appropriate).

#Interview Questions

Q1What is the Spring bean lifecycle?

Bean definition → instantiation → dependency injection → Aware interfaces → BeanPostProcessor (before) → @PostConstruct → afterPropertiesSet() → initMethod() → BeanPostProcessor (after) → bean ready → @PreDestroy → destroy() → destroyMethod().

Q2When is @PostConstruct executed?

After dependency injection is complete and before the bean is made available for use.

Q3When is @PreDestroy executed?

Just before a managed bean is destroyed during application shutdown.

Q4What is the difference between BeanPostProcessor and BeanFactoryPostProcessor?
BeanFactoryPostProcessorBeanPostProcessor
Operates on BeanDefinition metadataOperates on bean instances
Before bean creationBefore and after bean initialization
Q5Which scope is the default?

singleton.

Q6Does Spring destroy prototype beans?

No. Spring creates them but does not invoke destruction callbacks automatically.

Q7Which interface provides the afterPropertiesSet() callback?

InitializingBean.

Q8Which interface provides the destroy() callback?

DisposableBean.

Q9Why is BeanPostProcessor important?

It enables framework features such as proxy creation for @Transactional, @Async, caching and AOP.

Q10What is the initialization order?

Constructor → dependency injection → Aware methods → BeanPostProcessor (before) → @PostConstruct → afterPropertiesSet() → initMethod() → BeanPostProcessor (after).

#Key Takeaways

  • A Spring bean goes through a well-defined lifecycle from creation to destruction.
  • @PostConstruct is ideal for initialization after dependency injection.
  • @PreDestroy releases resources during shutdown.
  • BeanFactoryPostProcessor modifies metadata before instantiation; BeanPostProcessor customizes instances around initialization.
  • singleton is the default scope; prototype beans aren't destroyed by Spring.
  • Transactions, AOP, security, async execution and caching rely on BeanPostProcessor and proxies.