DEV Community

Azizul Arif
Azizul Arif

Posted on

Spring Beans and Bean Lifecycle: A Simple Practical Guide

Spring Beans and Bean Lifecycle: A Simple Practical Guide

If you are learning Spring Boot, one concept you will hear constantly is Spring Bean.

At first, it sounds simple:

"A Spring Bean is an object managed by Spring."

But what does "managed" actually mean?

Who creates the object?
When are its dependencies injected?
When does initialization happen?
What happens when the application shuts down?

Let's understand these questions with a simple example.


What Is a Spring Bean?

A Spring Bean is an object that is created and managed by the Spring IoC container.

For example:

@Service
public class PaymentService {

    public void pay() {
        System.out.println("Payment completed");
    }
}
Enter fullscreen mode Exit fullscreen mode

Because PaymentService is annotated with @Service, Spring can discover it during component scanning and register it as a bean.

We can then inject it into another class:

@Service
public class OrderService {

    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void placeOrder() {
        paymentService.pay();
        System.out.println("Order placed");
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice that we didn't write:

PaymentService paymentService = new PaymentService();
Enter fullscreen mode Exit fullscreen mode

Spring creates the PaymentService bean and injects it into OrderService.

This is Dependency Injection.


Bean vs Normal Java Object

This distinction is important.

If we write:

PaymentService service = new PaymentService();
Enter fullscreen mode Exit fullscreen mode

we have created a normal Java object.

Spring is not automatically managing it.

Therefore, Spring won't automatically manage its dependency injection or lifecycle callbacks.

But when Spring creates the object as a bean, Spring manages it.

A simple way to remember it:

Normal Java object
        ↓
Created by your code
        ↓
You manage it


Spring Bean
        ↓
Created/registered by Spring
        ↓
Spring manages it
Enter fullscreen mode Exit fullscreen mode

This is one of the practical reasons we use Dependency Injection instead of creating dependencies manually with new.


How Does Spring Create a Bean?

For a class such as:

@Service
public class PaymentService {
}
Enter fullscreen mode Exit fullscreen mode

Spring's simplified process looks like this:

Application starts
       ↓
Component scanning
       ↓
Spring discovers PaymentService
       ↓
Bean definition is created
       ↓
PaymentService is instantiated
       ↓
Dependencies are injected
       ↓
Initialization happens
       ↓
Bean is ready to use
Enter fullscreen mode Exit fullscreen mode

The exact internal process is more detailed, but this simplified flow is enough to understand the main idea.


What Is Bean Lifecycle?

A Spring Bean has a lifecycle.

The simplified lifecycle is:

Bean Definition
      ↓
Instantiation
      ↓
Dependency Injection
      ↓
Initialization
      ↓
Bean Ready
      ↓
Application Running
      ↓
Application Shutdown
      ↓
Destruction
Enter fullscreen mode Exit fullscreen mode

Let's look at the important stages.


1. Bean Instantiation

Spring creates an instance of the bean.

For example:

@Service
public class PaymentService {
}
Enter fullscreen mode Exit fullscreen mode

Spring creates an instance of PaymentService.

Conceptually:

Spring Container
      ↓
PaymentService instance
Enter fullscreen mode Exit fullscreen mode

2. Dependency Injection

Suppose OrderService depends on PaymentService:

@Service
public class OrderService {

    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}
Enter fullscreen mode Exit fullscreen mode

Spring provides the PaymentService dependency when creating OrderService.

The important sequence is:

Create PaymentService
       ↓
PaymentService becomes available
       ↓
Create OrderService
       ↓
Inject PaymentService
Enter fullscreen mode Exit fullscreen mode

This is why constructor injection is useful: the dependency is available when the object is constructed.


3. @PostConstruct

Sometimes a bean needs to perform some initialization after its dependencies have been injected.

We can use @PostConstruct:

@Service
public class PaymentService {

    @PostConstruct
    public void init() {
        System.out.println("PaymentService initialized");
    }
}
Enter fullscreen mode Exit fullscreen mode

@PostConstruct runs after the bean has been created and its dependencies have been injected.

So the simplified flow becomes:

Instantiate
    ↓
Inject dependencies
    ↓
@PostConstruct
    ↓
Bean ready
Enter fullscreen mode Exit fullscreen mode

For example, initialization could be used to load some required configuration or prepare resources.


4. Bean Is Ready

After initialization is complete, Spring considers the bean ready for normal use.

For example:

orderService.placeOrder();
Enter fullscreen mode Exit fullscreen mode

At this point:

  • The object has been created.
  • Its dependencies have been injected.
  • Initialization callbacks have completed.

The bean can now participate normally in the application.


5. Application Shutdown

What happens when the application stops?

For managed beans, Spring performs the appropriate destruction callbacks when the ApplicationContext is gracefully closed.

We can use @PreDestroy for cleanup:

@Service
public class PaymentService {

    @PreDestroy
    public void destroy() {
        System.out.println("PaymentService destroyed");
    }
}
Enter fullscreen mode Exit fullscreen mode

The simplified shutdown flow is:

ApplicationContext closes
        ↓
Destruction callbacks
        ↓
@PreDestroy
        ↓
Bean destroyed
Enter fullscreen mode Exit fullscreen mode

This can be useful when a bean needs to release resources or perform cleanup.


Dependency Order

There is another interesting part of the lifecycle.

Suppose:

@Service
public class OrderService {

    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}
Enter fullscreen mode Exit fullscreen mode

Here:

OrderService
      ↓
PaymentService
Enter fullscreen mode Exit fullscreen mode

OrderService depends on PaymentService.

Therefore, during startup, the dependency needs to be ready first:

PaymentService
      ↓
OrderService
Enter fullscreen mode Exit fullscreen mode

During destruction, the order is reversed:

OrderService
      ↓
PaymentService
Enter fullscreen mode Exit fullscreen mode

A simple mental model is:

Startup:
Dependency → Dependent

Shutdown:
Dependent → Dependency
Enter fullscreen mode Exit fullscreen mode

A Complete Example

Let's put the important lifecycle callbacks together:

@Service
public class PaymentService {

    @PostConstruct
    public void init() {
        System.out.println("Payment initialized");
    }

    public void pay() {
        System.out.println("Payment completed");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("Payment destroyed");
    }
}
Enter fullscreen mode Exit fullscreen mode

And:

@Service
public class OrderService {

    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    @PostConstruct
    public void init() {
        System.out.println("Order initialized");
    }

    public void placeOrder() {
        paymentService.pay();
        System.out.println("Order placed");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("Order destroyed");
    }
}
Enter fullscreen mode Exit fullscreen mode

The simplified lifecycle looks like:

Application starts
        ↓
PaymentService created
        ↓
PaymentService @PostConstruct
        ↓
OrderService created
        ↓
PaymentService injected
        ↓
OrderService @PostConstruct
        ↓
Application running
        ↓
Application shuts down
        ↓
OrderService @PreDestroy
        ↓
PaymentService @PreDestroy
        ↓
Beans destroyed
Enter fullscreen mode Exit fullscreen mode

The Main Idea

You don't need to memorize a long list of Spring lifecycle methods.

Start with this mental model:

Spring Container
       ↓
Creates the Bean
       ↓
Injects Dependencies
       ↓
Initializes the Bean
       ↓
Bean Ready
       ↓
Application Runs
       ↓
Application Shutdown
       ↓
Destroys the Bean
Enter fullscreen mode Exit fullscreen mode

And remember:

A Spring Bean is an object whose creation, dependencies, and lifecycle are managed by the Spring container.

Once this becomes clear, concepts like @Service, @Component, Dependency Injection, @PostConstruct, and @PreDestroy start fitting together naturally.

In a future article, we can take the next step and look at Spring Bean Scopes — especially Singleton vs Prototype — and see how they affect bean instances in a real application.

Top comments (0)