Medium🚀 Java Developer Switch Preparation #04: Spring Bean Lifecycle Explained with Example | by juhie sayyed | Oct, 2026 | Medium
Sitemap
juhie sayyed

juhie sayyed

🚀 Java Developer Switch Preparation #04: Spring Bean Lifecycle Explained with Example

7 min readOct 15, 2026

--

From object creation to initialization and destruction — understanding what happens behind the scenes.

🤔 A Question I Had While Learning Spring

In my previous blog, I explored how Spring discovers classes, registers bean definitions, creates beans, and connects dependencies.

But that raised another question:

When Spring creates a bean, is it immediately ready to use?

Not exactly.

Creating an object is only one part of its lifecycle. Spring also needs to populate its dependencies, perform initialization, and process the bean before it becomes fully available for use.

And when the application shuts down, Spring may need to perform cleanup as well.

To understand this better, let’s follow a bean through its lifecycle and explore what happens at each stage.

🎬 Meet Our PaymentService

Imagine we’re building a payment application.

Our PaymentService depends on a PaymentRepository to access payment data. Instead of creating the repository manually, we let Spring inject it.

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {
private final PaymentRepository repository;
public PaymentService(PaymentRepository repository) {
this.repository = repository;
System.out.println("PaymentService constructor called");
}
@PostConstruct
public void initialize() {
System.out.println("PaymentService initialized");
}
public void processPayment() {
System.out.println("Processing payment...");
}
@PreDestroy
public void cleanup() {
System.out.println("Cleaning up PaymentService");
}
}

Let’s focus on three parts of this class:

  • Constructor: Runs when the object is instantiated and receives the required dependency through constructor injection.
  • @PostConstruct: Marks a method that Spring invokes during bean initialization, after dependency injection.
  • @PreDestroy: Marks a method that Spring invokes during managed bean destruction.

Each serves a different purpose, and understanding when they execute is the key to understanding the bean lifecycle.

Note: This example uses jakarta.annotation imports, as commonly used with Spring Boot 3 and later.

🛤️ The Bean’s Journey Through Spring

Think of the bean lifecycle as a sequence of stages rather than just a collection of annotations.

Here’s a simplified view of what happens:

① Instantiation

Spring creates the bean instance by invoking its constructor.

⬇️

② Dependency Population

Spring supplies the required dependencies. With constructor injection, these dependencies are provided when the constructor is invoked. With field or setter injection, dependency population happens after instantiation.

⬇️

③ Initialization and Post-Processing

Spring invokes applicable initialization callbacks and processes the bean through its lifecycle infrastructure.

⬇️

④ Ready for Use

The fully initialized bean becomes available for use by the application. Depending on the configuration and infrastructure involved, Spring may expose a proxy instead of the original instance.

⬇️

⑤ Destruction

When the application context closes, Spring invokes the applicable destruction callbacks for beans whose destruction it manages.

This is a simplified overview. The actual lifecycle includes additional framework steps, and the exact processing depends on the bean’s configuration.

Now, let’s explore the stages that are particularly important to understand.

🔍 1. Constructor vs. @PostConstruct: What's the Difference?

This is one of the first distinctions worth understanding.

The constructor runs when the object is instantiated. In our example, Spring supplies the PaymentRepository through constructor injection.

After the bean has been instantiated and its dependencies have been populated, Spring invokes the @PostConstruct method during initialization.

For our example, the relevant output would be:

PaymentService constructor called
PaymentService initialized

The constructor runs first, followed by the initialization callback.

Why does this matter?

Suppose your initialization logic needs to use a dependency injected by Spring. A @PostConstruct method can be useful when that logic must run after dependency injection.

For example, it could validate configuration or prepare internal data structures using injected dependencies.

The key distinction:

  • The constructor participates in creating the object.
  • @PostConstruct participates in initializing the bean after dependency injection.

They are related stages, but they are not interchangeable.

🧩 2. What Happens Behind the Scenes? Meet BeanPostProcessor

So far, we’ve seen how Spring creates an object, injects its dependencies, and invokes initialization callbacks.

But Spring also provides an extension mechanism for processing bean instances: BeanPostProcessor.

A BeanPostProcessor allows Spring to process bean instances before and after their initialization callbacks.

Its two key methods are:

postProcessBeforeInitialization(...)
postProcessAfterInitialization(...)

You don’t need to implement a BeanPostProcessor in every application. Spring uses this extension mechanism as part of its infrastructure.

But why should we care about it?

How Does This Connect to Proxies?

Consider @Transactional, which we'll explore in more detail in a future blog.

Spring’s declarative transaction management can use proxies to apply transaction behavior around method calls.

For example, when another component calls a transactional method through the appropriate Spring-managed proxy, Spring can manage the transaction around that invocation.

This means the object exposed by Spring may be a proxy that wraps the underlying target object.

That’s an important connection: Spring doesn’t only manage object creation. Its infrastructure can also add behavior around objects without requiring us to implement that behavior manually in every service.

One technical detail is worth remembering: @PostConstruct is itself handled by Spring's lifecycle infrastructure, including a BeanPostProcessor. Different processors participate at different stages, so bean post-processing should not be understood as one single operation that happens entirely before or entirely after initialization.

Get juhie sayyed’s stories in your inbox

Join Medium for free to get updates from this writer.

For now, the takeaway is simple: BeanPostProcessor is an extension point that allows Spring to process beans around their initialization callbacks.

👋 3. What Happens When the Application Shuts Down?

We’ve followed our bean from creation to initialization. But what happens when the application no longer needs it?

Remember this method?

@PreDestroy
public void cleanup() {
System.out.println("Cleaning up PaymentService");
}

When Spring closes the application context, it invokes applicable destruction callbacks for managed beans.

This gives a bean an opportunity to release resources or perform cleanup.

For example, a component might need to close a resource it opened or release resources it owns.

However, there are two important limitations to understand.

First, cleanup isn’t guaranteed under every circumstance.

An abrupt process termination or system crash can prevent destruction callbacks from running.

Second, destruction depends on the bean’s scope and lifecycle management.

Spring does not automatically invoke destruction callbacks for prototype-scoped beans after handing those instances to the caller. If a prototype bean owns resources that need cleanup, the caller must arrange that cleanup.

For the usual managed singleton bean, remember:

  • @PostConstruct → initialization.
  • @PreDestroy → cleanup during managed destruction.

These annotations represent different stages of the bean’s lifecycle.

⚙️ 4. Are @PostConstruct and @PreDestroy the Only Options?

No. Spring supports several mechanisms for initialization and destruction.

MechanismPurpose@PostConstructMarks an initialization callbackInitializingBeanProvides the afterPropertiesSet() callbackinitMethodConfigures an initialization method through @Bean@PreDestroyMarks a destruction callbackDisposableBeanProvides the destroy() callbackdestroyMethodConfigures a destruction method through @Bean

For most application code, @PostConstruct and @PreDestroy are convenient choices. The lifecycle interfaces are alternatives, while initMethod and destroyMethod are useful when configuring beans through Java configuration.

When Is @Bean Useful for Lifecycle Configuration?

In Blog #03, we explored how @Bean can register an object returned by a configuration method, including objects from third-party libraries.

Suppose an external library provides a PaymentClient. We cannot modify its source code to add Spring annotations, but we can configure its lifecycle through a @Bean method.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PaymentConfiguration {
@Bean(
initMethod = "initialize",
destroyMethod = "cleanup"
)
public PaymentClient paymentClient() {
return new PaymentClient();
}
}

This example assumes that PaymentClient provides the initialize() and cleanup() methods.

Spring can invoke these configured methods at the appropriate lifecycle stages.

Notice how this connects to our previous blog: @Bean gives us explicit control over how an object is registered and configured, even when we don't own the class itself.

What If Multiple Lifecycle Mechanisms Are Configured?

This is a useful interview detail.

For a bean using distinct initialization callbacks, the standard order is:

  1. @PostConstruct
  2. InitializingBean.afterPropertiesSet()
  3. The configured custom initialization method

For destruction, the corresponding order is:

  1. @PreDestroy
  2. DisposableBean.destroy()
  3. The configured custom destruction method

These sequences apply when the respective mechanisms are configured and their callbacks are invoked. You don’t need to use every mechanism in the same class.

The important thing is to understand that Spring offers multiple ways to handle initialization and cleanup, depending on the requirements.

💡 5. Three Things I Want to Remember for Interviews

After exploring the bean lifecycle, these are three distinctions I want to be able to explain in my own words rather than simply memorize.

1. Object creation isn’t the entire lifecycle.

Spring still needs to populate dependencies, invoke initialization callbacks, and perform applicable processing.

2. BeanPostProcessor and @PostConstruct serve different purposes.

BeanPostProcessor is an extension mechanism for processing bean instances before and after initialization callbacks. @PostConstruct marks a specific initialization callback.

3. Bean destruction depends on lifecycle management and scope.

Spring invokes applicable destruction callbacks when it manages the bean’s destruction. Prototype-scoped beans are an important exception to automatic destruction management.

Understanding these distinctions makes it easier to explain not just what Spring does, but why its lifecycle mechanisms exist.

🎯 Quick Interview Check

Before moving to the next topic, try answering these questions without looking back.

Q1. What is the difference between a constructor and @PostConstruct?

Q2. When does Spring invoke @PreDestroy?

Q3. What is a BeanPostProcessor, and how does it differ from an initialization callback?

Q4. Why might Spring expose a proxy instead of the original bean instance?

Q5. Does Spring automatically invoke destruction callbacks for prototype-scoped beans?

Q6. What are the standard initialization and destruction callback orders when multiple distinct mechanisms are configured?

If you can explain the reasoning behind each answer, you’re moving beyond memorizing annotations and toward understanding Spring’s internal behavior.

✍️ Final Thoughts

When I first started learning Spring, I focused mainly on what annotations meant and where to use them.

Now, I’m trying to look one step deeper: what actually happens behind the scenes when Spring manages an object?

Understanding the bean lifecycle helped me connect object creation, dependency injection, initialization callbacks, post-processing, proxies, and cleanup into one bigger picture.

That’s the approach I want to continue throughout this Java Developer Switch Preparation series: learning concepts, connecting them to code, and understanding why Spring behaves the way it does.

If this article helped you understand the bean lifecycle a little better, feel free to leave a clap 👏 or share your thoughts in the comments.

Thanks for reading! See you in the next blog. Until then, keep learning, building, and growing. 🚀

Coming Next: Blog #05

Spring Bean Scopes Explained: Singleton vs. Prototype

Why does Spring usually reuse the same bean instance? When would we want a new instance instead? That’s what I’ll explore next.

Learning in public, one concept at a time.

#Java #SpringBoot #SpringFramework #JavaDeveloper #InterviewPreparation #SoftwareEngineering #LearningInPublic #BackendDevelopment

--

--