What is Bean Scope?
Do NOT store mutable state in Singleton beans (e.g., user cache in a service field). This will lead to race conditions and memory leaks. Instead:
Junior Level
Scope defines how many instances of a bean Spring creates.
Main scopes:
| Scope | How many instances |
|---|---|
| Singleton | One for the entire application (default) |
| Prototype | New on each request |
@Service // Singleton by default
public class UserService { }
@Scope("prototype")
@Service
public class ReportGenerator { }
Example:
// Singleton: always the same
UserService s1 = context.getBean(UserService.class);
UserService s2 = context.getBean(UserService.class);
s1 == s2 // -> true
// Prototype: new each time
ReportGenerator r1 = context.getBean(ReportGenerator.class);
ReportGenerator r2 = context.getBean(ReportGenerator.class);
r1 == r2 // -> false
Middle Level
Singleton
// By default!
@Service
public class UserService { }
// One instance per ApplicationContext
// All threads use the same object
// -> Must be Stateless!
Prototype
@Scope("prototype")
@Service
public class ReportGenerator { }
// New instance on EVERY getBean()
// Spring creates it but does NOT manage destruction!
// -> @PreDestroy is NOT called
Web Scopes
@Scope("request") // One instance per HTTP request
@Scope("session") // One instance per HTTP session
@Scope("application") // One per ServletContext
Prototype in Singleton Problem
// Prototype created ONCE!
@Service
public class SingletonService {
@Autowired
private PrototypeBean prototype; // Stuck with one instance!
}
// ObjectProvider
@Service
public class SingletonService {
@Autowired
private ObjectProvider<PrototypeBean> provider;
public void process() {
PrototypeBean bean = provider.getObject(); // New each time!
}
}
Senior Level
When NOT to Use Singleton with Mutable State
Do NOT store mutable state in Singleton beans (e.g., user cache in a service field). This will lead to race conditions and memory leaks. Instead:
- Extract state to external storage (Redis, DB)
- Use Request scope for request data
- Use Prototype if you need a new instance
Scoped Proxy
// Solution to Prototype/Request in Singleton problem
@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences preferences() {
return new UserPreferences();
}
// Spring injects a proxy into Singleton
// Proxy accesses RequestContextHolder (ThreadLocal) on each call, which stores the current HTTP request attributes. Thus, even a singleton bean injecting a request-scoped bean gets the current object for each request.
Under the Hood: Where Beans Are Stored
Singleton -> DefaultSingletonBeanRegistry (ConcurrentHashMap)
Request -> RequestContextHolder (ThreadLocal) - ScopedProxyMode.TARGET_CLASS creates a CGLIB proxy that accesses the current scope (request/session) on each call. RequestContextHolder stores current HTTP request data in ThreadLocal.
Session -> SessionAttributes
Prototype -> Created, not stored
Custom Scope
// Your own scope!
public class ThreadScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();
public Object get(String name, ObjectFactory<?> factory) {
return threadLocal.get().computeIfAbsent(name, k -> factory.getObject());
}
}
// Registration
context.getBeanFactory().registerScope("thread", new ThreadScope());
Production Experience
Real scenario: Request bean in Singleton
// Singleton received Request bean at startup
// -> All requests use the first user's data!
// ScopedProxyMode.TARGET_CLASS
// -> Proxy -> RequestContextHolder -> actual bean
Best Practices
- Singleton - by default (Stateless!)
- Prototype - rarely. If you just need a new object without Spring dependencies - use
new. But if the object requires dependency injection - Prototype is justified. - Request/Session - for web data
- Scoped Proxy - for injecting narrow scopes into wider ones
- ObjectProvider - alternative to Scoped Proxy. ObjectProvider vs ScopedProxy: ObjectProvider - explicit
getObject()call in code (more readable). ScopedProxy - proxy is transparently substituted (less obvious but more convenient). ObjectProvider is preferred for explicit control. - Avoid state in Singleton
Summary for Senior
- Singleton = one per application, Stateless
- Prototype = new each time, @PreDestroy does NOT work
- Request/Session = web scope
- Scoped Proxy = proxy -> RequestContextHolder
- ObjectProvider = explicit bean request
- Custom Scope = your own lifecycle
Interview Cheat Sheet
Must know:
- Singleton (default) - one instance per ApplicationContext, must be Stateless
- Prototype - new instance on each request,
@PreDestroyis NOT called, Spring does not manage destruction - Web scopes:
request(per HTTP request),session(per HTTP session),application(per ServletContext) - Prototype in Singleton problem: prototype is created ONCE when injected - use
ObjectProvider.getObject() - Scoped Proxy (
ScopedProxyMode.TARGET_CLASS) - CGLIB proxy accessesRequestContextHolder(ThreadLocal) on each call - Avoid mutable state in Singleton - race conditions and memory leaks
- Custom Scope - can create your own (e.g.,
ThreadScopeviaThreadLocal) - Singleton stores beans in
DefaultSingletonBeanRegistry(ConcurrentHashMap), Request - inThreadLocal
Common follow-up questions:
- How to inject Prototype into Singleton?
ObjectProvider<PrototypeBean>.getObject()- new each time, or Scoped Proxy. - What is a Scoped Proxy? A proxy that accesses the actual scope (request/session) via
RequestContextHolderon each call. - Why must Singleton be Stateless? One object is used by all threads - mutable state = race condition.
- When to use Prototype instead of
new? When the object requires dependency injection from Spring.
Red flags (DO NOT say):
- “Singleton can store mutable state” (race conditions and memory leaks)
- “Prototype beans are destroyed automatically” (
@PreDestroyis NOT called, clean manually) - “Request/Session scope works without Scoped Proxy in Singleton” (will be one instance for all requests!)
- “ObjectProvider and Scoped Proxy are the same thing” (ObjectProvider - explicit
getObject(), ScopedProxy - transparent substitution)
Related topics:
- [[06. What is Bean Lifecycle]]
- [[07. What are the Bean Lifecycle stages]]
- [[09. What do PostConstruct and PreDestroy methods do]]
- [[04. What is a Bean in Spring]]
- [[01. What is Dependency Injection]]