Start Learning
Javaneer
Back to stage
Module 0·Spring Foundations

Beans, Scopes & the Lifecycle

What a bean actually is, the scopes it can live in, and the lifecycle callbacks from instantiation to destruction.

16 min readIntermediate
On this page

A bean is simply an object that the Spring container creates, wires, and manages. That's the whole definition. Your BookService, once registered with the container, is a bean. The interesting questions are: how many of each bean exist, and what happens from birth to death.

Scopes: how many instances?

A bean's scope decides how many instances the container hands out.

ScopeMeaning
singleton (default)One shared instance for the entire container. Everyone who needs it gets the same object.
prototypeA new instance every time the bean is requested.
requestOne instance per HTTP request (web apps).
sessionOne instance per HTTP session (web apps).
applicationOne instance per servlet context (web apps).
@Service                    // singleton by default
class BookService { }

@Service
@Scope("prototype")         // a fresh instance on every injection/lookup
class ReportBuilder { }

Singleton means shared - so keep beans stateless

Because the default singleton is shared across the whole application (and across threads), a bean should not hold mutable per-user state in fields. Keep beans stateless; pass request-specific data as method arguments. This is the single most common Spring beginner bug.

Singleton vs prototype: the office printer vs sticky notes

The singleton bean is the office printer: there's one, everybody shares it, and it had better not keep one person's document lying around for the next person. A prototype bean is a sticky note: you tear off a brand-new one every time you need it, use it, and throw it away.

The lifecycle: birth to death

For a singleton, the container runs a precise sequence when it starts up (and when it shuts down). You can hook into the important moments:

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

@Service
class SearchIndex {

    private final BookRepository repository;

    SearchIndex(BookRepository repository) {   // 1. constructed & dependencies injected
        this.repository = repository;
    }

    @PostConstruct                              // 2. runs after wiring is complete
    void warmUp() {
        // safe to use injected dependencies here - build the initial index
    }

    @PreDestroy                                 // 3. runs on container shutdown
    void flush() {
        // release resources, flush caches
    }
}

The order matters: @PostConstruct runs after all dependencies are injected, so it's the right place for initialization that needs collaborators. A constructor runs before the object is fully part of the container, so heavy init belongs in @PostConstruct, not the constructor.

The bean lifecycleStep through what the container does to a singleton bean, from construction to destruction.
new BookService(...)

The container calls the constructor and creates the raw bean instance. With constructor injection, dependencies are passed in right here.

1 / 7

Prototype beans get no shutdown callback

Spring fully manages the singleton lifecycle, including @PreDestroy. For prototype beans it steps back after creation - it won't call @PreDestroy, because it isn't tracking every copy. Clean those up yourself.

Where does the index get built?

You have a SearchIndex bean that needs its injected BookRepository to build an index at startup. A teammate puts the index-building code in the constructor. Why is @PostConstruct the safer choice - and what could go wrong relying on field injection plus a constructor here?

What is the default scope of a Spring bean, and what does it imply?

Key takeaways

  • A bean is any object the Spring container creates, wires, and manages.
  • Scope controls instance count: singleton (one, shared - the default), prototype (new each time), plus web scopes request/session/application.
  • Singletons are shared across threads, so keep beans stateless - a top source of subtle bugs.
  • The lifecycle runs construct -> inject -> @PostConstruct -> in use -> @PreDestroy; do startup work in @PostConstruct, after dependencies are wired.
  • Spring calls @PreDestroy for singletons but not for prototype beans.
Was this lesson helpful?
Edit this page on GitHub