Spring Boot path · The container · Topic 2 of 12

Bean scopes and lifecycle

Not started

A bean's scope says how many instances the container makes and how long each lives. The default, singleton, means exactly one instance per application context, created at start-up and shared by every caller. In a web application that means every request thread runs through the same object at the same time, so the rule of thumb is simple: singletons hold collaborators and configuration, never per-request state.

Why it matters: Most beans are shared by every request at once, so a single mutable field in a singleton is a data race waiting for traffic.

  1. 1Singleton is the default

    One instance per container, shared by every thread that serves a request. Keep it stateless.

  2. 2Other scopes are opt-in

    Prototype gives a new instance per lookup; request and session scopes give one per HTTP request or session.

  3. 3Creation has steps

    Construct, inject, run post-processors and @PostConstruct, then the bean is ready. On shutdown, @PreDestroy runs.

  4. 4Post-processors make proxies

    The step after initialisation is where Spring may swap your object for a proxy that adds behaviour around it.

1

How it works

The explanation, with examples you can run.

The scopes you will meet

ScopeInstancesTypical use
singleton (default)One per containerServices, repositories, clients
prototypeA new one each time it is requested from the containerStateful helpers, builders
requestOne per HTTP requestPer-request context
sessionOne per HTTP sessionShopping basket in a server-rendered app
applicationOne per ServletContextRarely needed
@Component@Scope("prototype")class ReportBuilder { private final List<String> lines = new ArrayList<>(); } @Serviceclass ReportService {  private final ObjectProvider<ReportBuilder> builders;   ReportService(ObjectProvider<ReportBuilder> builders) { this.builders = builders; }   String build() {    ReportBuilder b = builders.getObject();   // fresh instance each call    ...  }}

Injecting the prototype directly into the constructor would give the singleton one builder for life, which is a classic surprise. ObjectProvider (or a scoped proxy, which is how request and session beans can be injected into singletons) defers the lookup to the moment you need it.

From constructor to destruction

For each singleton, the container goes through the same steps:

  1. Call the constructor (with constructor-injected dependencies).
  2. Inject fields and setters, if any.
  3. Run BeanPostProcessors before initialisation, then the init callbacks: @PostConstruct first, then InitializingBean.afterPropertiesSet(), then a custom init method.
  4. Run BeanPostProcessors after initialisation. This is where Spring may return a proxy instead of your object, for @Transactional, @Async, @Cacheable and similar.
  5. The bean is ready. On a clean shutdown, @PreDestroy (and other destroy callbacks) run in reverse order.
@Componentclass PriceCache {  @PostConstruct  void warm() { /* every dependency is set by now */ }   @PreDestroy  void flush() { /* runs on graceful shutdown */ }}

In Boot 3 these annotations come from jakarta.annotation, not javax.annotation. One detail: Spring does not track prototype beans after handing them out, so their destroy callbacks are not called; cleaning them up is your job.

Why step 4 matters

The object other beans receive may not be the class you wrote, but a generated subclass that wraps it. Most of the surprises in the transactions topic, such as an annotation that "does nothing", come from forgetting that the proxy exists and when it is bypassed.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.A singleton @Service keeps the current user's id in an instance field set at the start of each request. What goes wrong under load?
  2. 2.A singleton is injected with a prototype-scoped bean in its constructor. How many prototype instances does it use over its life?
  3. 3.In what order run a constructor, @PostConstruct and field injection for one bean?

0 of 3 right so far. Get all three to finish this topic.