Foundation · Understand
Bean Scopes
Singleton is the default and usually right; prototype and web scopes need a reason plus a strategy for crossing scope boundaries.
Primary source: Spring Framework 6.2 Reference: Bean Scopes
Lesson 9 of 14
1d streakThe Architectural Question
A singleton holds one reference forever, so injecting a request-scoped or prototype bean freezes whatever instance happened to exist at startup.
What this guide proves
- Singleton
- Prototype
- Scoped Proxies
Container scopes: Identity boundaries in memory
A bean's scope defines its lifecycle and instance reuse policy within the IoC container. Spring defines five standard scopes:
| Scope | Identity Boundary | Creation Timing | Destruction Managed by Spring? |
|---|---|---|---|
| singleton (default) | One instance per BeanDefinition per ApplicationContext | Eagerly at startup (refresh) | Yes (on context shutdown) |
| prototype | A new instance every time requested from container | Lazily on each getBean() lookup | NO (Spring never calls @PreDestroy on prototypes!) |
| request | One instance per HTTP request lifecycle (Web only) | On demand per incoming HTTP request | Yes (at end of HTTP request) |
| session | One instance per HTTP HttpSession lifecycle (Web only) | On demand per HTTP session | Yes (when HttpSession is invalidated) |
| application | One instance per ServletContext (Web only) | On ServletContext initialization | Yes (when ServletContext is destroyed) |
Architectural Boundary · Do Not Confuse
GoF Singleton Pattern (JVM-wide single instance)
Spring Singleton Scope (Container-wide definition instance)
A classic GoF singleton restricts instantiation to one instance per classloader using a private constructor. A Spring singleton restricts instantiation to one instance per BeanDefinition within an ApplicationContext. You can easily create multiple Spring singleton instances of the same class by registering two distinct bean definitions.
The prototype scope & the memory leak trap
The most dangerous aspect of the prototype scope is that Spring does not manage the complete lifecycle of prototype beans. Spring creates the prototype instance, populates its properties, executes its BeanPostProcessor and @PostConstruct callbacks, and hands the instance over to the client. After handoff, the container retains NO reference to the prototype.
The Prototype Memory Leak Trap
Crossing scope boundaries: Scoped proxies for web requests
What happens when a Singleton service depends on a @RequestScope bean? The singleton service is constructed once at application boot time—when NO HTTP request exists! Directly injecting the request bean into the singleton constructor would crash startup.
To resolve this, Spring uses Scoped Proxies (proxyMode = ScopedProxyMode.TARGET_CLASS). Spring injects a CGLIB proxy into the singleton. When the singleton invokes a method on the proxy, the proxy looks up the current thread's RequestAttributes and delegates to that specific HTTP request's bean instance.
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserContext {
private String userId;
private String tenantId;
// Proxied into singleton services cleanly!
}Break it deliberately: The singleton-prototype mismatch bug
Directly injecting a prototype bean into a singleton service causes the prototype to be instantiated only ONCE—when the singleton is constructed. The prototype behaves like a singleton, silently breaking stateful logic.
@Component
@Scope("prototype")
public class RequestTracker {
private final UUID requestId = UUID.randomUUID();
public UUID getId() { return requestId; }
}
@Service
public class OrderService {
private final RequestTracker tracker; // Injected ONCE at boot time!
public OrderService(RequestTracker tracker) {
this.tracker = tracker;
}
public void process() {
// Tracker requestId is identical for EVERY user and request!
System.out.println("Processing with ID: " + tracker.getId());
}
}The Fix: Use ObjectProvider<T>
Check your understanding
Active Recall Checkpoint
Why does Spring never invoke @PreDestroy lifecycle callbacks on prototype-scoped beans?
Hint: Think about object references and garbage collection.
Show answer & explanation
The container does not retain references to prototype instances after handing them to callers (to prevent memory leaks). Because the container does not hold them, it cannot track when they are discarded, leaving destruction management entirely to the caller.
Active Recall Checkpoint
How does a Scoped Proxy allow a singleton service to collaborate with a request-scoped bean?
Hint: Consider CGLIB delegation and thread-local storage.
Show answer & explanation
Spring injects a dynamic CGLIB proxy into the singleton. When methods are invoked, the proxy dynamically queries the current thread RequestAttributes to locate and delegate to the active HTTP request instance.
Runtime Architecture & Flow
Decision canvas
Evaluate the choice across three constraints
These are comparison criteria, not sequential steps. A strong answer states which constraint dominates in the current system.
Singleton
Ask how each option changes this guarantee, its operational cost, and its failure mode.
Prototype
Ask how each option changes this guarantee, its operational cost, and its failure mode.
Scoped Proxies
Ask how each option changes this guarantee, its operational cost, and its failure mode.
Decision prompt: Inject a Prototype bean into a Singleton bean and observe that the Prototype is only instantiated once (the scoped-proxy problem).
Working Implementation
Code Walkthrough
Inject a Prototype bean into a Singleton bean and observe that the Prototype is only instantiated once (the scoped-proxy problem).
package com.example.core;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;
public class ScopeBoundaryLab {
// Prototype bean: fresh instance created each time requested
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class RequestTraceContext {
private static final AtomicInteger SEQ = new AtomicInteger(1);
private final int traceId = SEQ.getAndIncrement();
public int getTraceId() { return traceId; }
}
// Singleton bean holding an ObjectProvider to prevent prototype capture
@Component
public static class IngestionService {
private final ObjectProvider<RequestTraceContext> traceContextProvider;
public IngestionService(ObjectProvider<RequestTraceContext> traceContextProvider) {
this.traceContextProvider = traceContextProvider;
}
public void handleEvent(String eventName) {
// Defer resolution to runtime invocation: new prototype per call
RequestTraceContext context = traceContextProvider.getObject();
System.out.printf("Event: %s | TraceId: %d%n", eventName, context.getTraceId());
}
}
}Synthesis & Production Verification
Production Acceptance Criteria
- Demonstrate prototype-in-singleton capturing exactly one instance
- Fix it with ObjectProvider and show a new instance per call
- Name the built-in scopes and where request and session apply
Key Takeaway
Spring singletons are scoped per BeanDefinition per container. Prototype beans are instantiated on demand but never destroyed by Spring, requiring caller resource management.
Architectural & Technical Review
Core Interview Questions
Q01How does a Spring singleton scope differ fundamentally from the classic Gang of Four (GoF) singleton pattern?Show answer
A GoF singleton restricts instantiation to one instance per classloader via private constructors. A Spring singleton restricts instantiation to one instance per BeanDefinition within an ApplicationContext.
Q02Why does Spring never invoke @PreDestroy lifecycle callbacks on prototype-scoped beans, and how can resource leaks be avoided?Show answer
The container does not retain references to prototype instances after handing them to callers. Because Spring does not track them, it cannot execute @PreDestroy, leaving resource destruction entirely to the caller.
Q03Why does directly injecting a request-scoped bean into a singleton service require a Scoped Proxy?Show answer
A singleton service is created once at application startup when no HTTP request exists. A CGLIB Scoped Proxy delegates method calls to the current thread's RequestAttributes on demand.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Directly injecting a prototype into a singleton — The prototype is injected only once at singleton creation time, causing it to freeze and act as an accidental singleton.
- Pitfall: Allocating unmanaged resources in prototype beans — Spring never invokes @PreDestroy on prototypes, causing file descriptor, socket, or thread leaks if callers do not manually close them.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources