Spring manages bean instances through scopes — rules that determine how many instances are created and how long they live. Understanding scopes is essential for the VMware Spring Professional exam (2V0-72.22), where questions on singleton vs prototype behavior, scoped proxies, and web scopes appear regularly.
All 6 Spring Bean Scopes at a Glance
| Scope | Instances | Lifecycle Managed | Available In |
|---|---|---|---|
singleton | One per ApplicationContext | Full (creation + destruction) | All contexts |
prototype | New instance per request | Partial (no destruction) | All contexts |
request | One per HTTP request | Full | Web contexts only |
session | One per HTTP session | Full | Web contexts only |
application | One per ServletContext | Full | Web contexts only |
websocket | One per WebSocket session | Full | Web contexts only |
The first two (singleton and prototype) are available in any Spring application. The remaining four require a web-aware ApplicationContext such as WebApplicationContext.
Singleton Scope (Default)
The singleton scope creates one shared instance per ApplicationContext. Every call to getBean() and every @Autowired injection returns the same object reference.
@Component
// @Scope("singleton") — this is the default, no annotation needed
public class OrderService {
// One instance shared across the entire application
}When to use singleton
Use for stateless services — repositories, service classes, configuration beans, and any component that does not hold per-user or per-request state.
Thread safety
Singleton beans are shared across all threads. Spring does not make singletons thread-safe automatically — that is your responsibility. If a singleton bean holds mutable state, concurrent access will cause bugs.
@Component
public class CounterService {
private int count = 0; // NOT thread-safe in a singleton!
public void increment() {
count++; // Race condition under concurrent access
}
}To avoid issues: keep singletons stateless, or use AtomicInteger, synchronized, or ConcurrentHashMap for shared mutable state.
Eager vs lazy initialization
Singleton beans are created eagerly by default — at ApplicationContext startup. You can change this with @Lazy:
@Component
@Lazy
public class ExpensiveService {
// Created only when first requested, not at startup
}Exam tip: Singleton scope is per-
ApplicationContext, not per-JVM. Two separate contexts in the same JVM (e.g. in a test) create two different singleton instances.
Prototype Scope
A new instance is created every time the bean is requested — via getBean(), @Autowired, or any other injection point.
@Component
@Scope("prototype")
public class ShoppingCart {
private final List<Item> items = new ArrayList<>();
// Each injection gets a fresh cart
}When to use prototype
Use for stateful beans that should not be shared — shopping carts, request-specific command objects, or builders that accumulate state.
Spring does not manage prototype destruction
This is frequently tested on the exam. Spring creates the prototype bean and hands it off — but it does not track it afterward. This means:
@PreDestroymethods are never called on prototype beansDisposableBean.destroy()is never called- You must clean up resources manually
Injecting prototype into singleton — the classic trap
If you inject a prototype bean into a singleton, the prototype is resolved once at singleton initialization — so you always get the same instance. This defeats the purpose of prototype scope.
@Component
public class SingletonService {
@Autowired
private ShoppingCart cart; // Always the SAME instance — bug!
}Three solutions:
1. @Lookup method injection — Spring overrides the method at runtime to return a fresh prototype each time:
@Component
public abstract class SingletonService {
@Lookup
protected abstract ShoppingCart createCart();
public void handleRequest() {
ShoppingCart cart = createCart(); // New instance every call
}
}2. ObjectProvider<T> — a lazy, safe injection point:
@Component
public class SingletonService {
private final ObjectProvider<ShoppingCart> cartProvider;
public SingletonService(ObjectProvider<ShoppingCart> cartProvider) {
this.cartProvider = cartProvider;
}
public void handleRequest() {
ShoppingCart cart = cartProvider.getObject(); // New instance every call
}
}3. ApplicationContext.getBean() — works, but couples your code to the Spring API:
@Component
public class SingletonService {
@Autowired
private ApplicationContext context;
public void handleRequest() {
ShoppingCart cart = context.getBean(ShoppingCart.class); // New instance
}
}Exam tip:
ObjectProvideris the recommended approach in modern Spring.@Lookuprequires the class to be non-final (Spring creates a CGLIB subclass).getBean()is the least preferred option.
Request Scope
One bean instance per HTTP request. The bean is created when the request arrives and destroyed when the response is sent.
@Component
@RequestScope // shorthand for @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
private String correlationId;
private Instant startTime = Instant.now();
// Each HTTP request gets its own instance
// Automatically destroyed after the response is sent
}Use for: per-request data — correlation IDs, request timing, audit information, user-specific request state.
Session Scope
One bean instance per HTTP session. Persists across multiple requests from the same user until the session expires or is invalidated.
@Component
@SessionScope // shorthand for @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserPreferences {
private String theme = "dark";
private String language = "en";
// Same instance across all requests in the user's session
}Use for: per-user state — preferences, shopping cart contents, wizard step tracking.
Application Scope
One instance per ServletContext — shared across all users and requests within the web application.
@Component
@ApplicationScope
public class GlobalRateLimiter {
private final AtomicInteger requestCount = new AtomicInteger(0);
// Shared across ALL users — similar to singleton but scoped to the ServletContext
}The difference from singleton: a singleton is per-ApplicationContext, while application scope is per-ServletContext. In most Spring Boot apps, these are equivalent. The distinction matters when multiple ApplicationContext instances share one ServletContext (e.g. in a parent-child context setup).
WebSocket Scope
One bean instance per WebSocket session. The bean lives as long as the WebSocket connection is open.
@Component
@Scope("websocket")
public class ChatSession {
private final List<String> messages = new ArrayList<>();
// One instance per WebSocket connection
}Use for: per-connection state in WebSocket applications — chat sessions, live dashboards, real-time collaboration.
Exam note: WebSocket scope is the least commonly tested of the six scopes. Know that it exists and requires a WebSocket-aware context.
Scoped Proxies
When you inject a shorter-lived scoped bean (like request) into a longer-lived bean (like singleton), Spring needs a scoped proxy to ensure each access resolves to the correct instance.
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
private String userId;
}
@Component // singleton
public class AuditService {
@Autowired
private RequestContext requestContext;
// This is actually a CGLIB proxy — each method call delegates
// to the real RequestContext for the current HTTP request
}Proxy modes
| Mode | Behavior |
|---|---|
ScopedProxyMode.TARGET_CLASS | Creates a CGLIB subclass proxy (default for @RequestScope, @SessionScope) |
ScopedProxyMode.INTERFACES | Creates a JDK dynamic proxy (bean must implement an interface) |
ScopedProxyMode.NO | No proxy — will fail if injected into a longer-lived scope |
The convenience annotations @RequestScope, @SessionScope, and @ApplicationScope all default to ScopedProxyMode.TARGET_CLASS — so you typically don't need to configure this manually.
Exam tip: Without a scoped proxy, injecting a
request-scoped bean into asingletonthrows an exception at startup because there is no active HTTP request during singleton initialization.
Lifecycle and Destruction by Scope
Understanding which lifecycle callbacks Spring invokes per scope is a key exam topic:
| Scope | @PostConstruct called? | @PreDestroy called? |
|---|---|---|
singleton | Yes, at context startup | Yes, at context shutdown |
prototype | Yes, at each creation | No — Spring does not track prototype beans |
request | Yes, when request starts | Yes, when request ends |
session | Yes, when session starts | Yes, when session is invalidated or expires |
application | Yes, at context startup | Yes, at context shutdown |
websocket | Yes, when connection opens | Yes, when connection closes |
The prototype scope is the only one where Spring does not manage destruction. For all other scopes, both initialization and destruction callbacks are invoked.
Exam Quick Reference
Q: What is the default scope?
singleton
Q: Which scopes require a web-aware ApplicationContext?
request, session, application, websocket
Q: A prototype bean injected into a singleton bean — how many instances are created?
Just one, at the time the singleton is initialized. Use @Lookup or ObjectProvider to get a new prototype each time.
Q: Does Spring call @PreDestroy on prototype beans?
No. Spring does not manage the full lifecycle of prototype beans — only creation and initialization.
Q: What happens if you inject a request-scoped bean into a singleton without a scoped proxy? An exception is thrown at startup because no HTTP request is active when the singleton is being created.
Q: What is the difference between application scope and singleton scope?
singleton is per-ApplicationContext, application is per-ServletContext. In most Spring Boot apps they behave the same, but the distinction matters in parent-child context configurations.
Frequently Asked Questions
What is the default bean scope in Spring?
The default scope is singleton — Spring creates one instance per ApplicationContext and shares it across all injection points. You do not need to declare it explicitly. This applies to both @Component-scanned beans and @Bean method definitions. To change the scope, use the @Scope annotation.
When should I use prototype scope instead of singleton?
Use prototype when the bean holds mutable state that should not be shared — for example, a shopping cart, a request-specific DTO builder, or a command object. If the bean is stateless (most service and repository classes), stick with singleton. Remember that Spring does not call @PreDestroy on prototype beans, so you must handle resource cleanup yourself.
What is a scoped proxy and when do I need one?
A scoped proxy is a wrapper that Spring creates around a shorter-lived scoped bean (like request or session) so it can be safely injected into a longer-lived bean (like singleton). Without a proxy, Spring would try to resolve the scoped bean at singleton creation time — when no HTTP request or session exists — and throw an exception. The @RequestScope and @SessionScope annotations enable proxying by default.
How do I inject a prototype bean into a singleton correctly?
The three recommended approaches are: ObjectProvider<T> (most modern and flexible), @Lookup method injection (Spring overrides the method via CGLIB), or ApplicationContext.getBean() (least preferred as it couples code to the Spring API). Direct @Autowired injection creates only one prototype instance, which is almost always a bug. See our Spring Bean Lifecycle guide for more on how injection timing affects bean behavior.
→ Test your knowledge with practice questions — 10 exam-style questions with detailed explanations
→ Spring Bean Lifecycle explained — full initialization and destruction callback order per scope
→ Spring Boot Auto-configuration explained — how @EnableAutoConfiguration discovers and loads beans
→ Get Full Access — 970+ Questions — full practice exam bank with detailed explanations