Spring Bean Scopes: 5 Types, Common Pitfalls and Concurrency Gotchas
The article explains the five Spring Bean scopes, clarifies that scope only controls instance count and not thread safety, and walks through real‑world concurrency bugs such as mutable singleton fields, prototype beans losing their prototype nature, and request‑scoped beans failing in async threads, offering concrete fixes and usage recommendations.
1. Bean Scopes
Singleton
Explanation: One instance for the whole application, shared by all requests and threads.
Creation time: Eagerly created at application startup (non‑lazy), giving the best performance.
Typical usage: Controllers, Services, Mappers, Components are singleton by default.
Pitfall: The singleton itself is safe; problems arise when mutable member variables are added, which causes most concurrency data‑mixing bugs.
Prototype
Explanation: A new instance is created each time the bean is requested from the container.
Hidden facts:
Spring does not manage prototype bean destruction; the container only initializes the bean, so manual resource release is required.
Prototype beans are not stored in Spring’s first‑level cache, therefore circular‑dependency fallback logic is not triggered.
Use case: Store independent data that must not be shared across threads, such as temporary task context or export parameters.
Request
Explanation: One bean instance per HTTP request; the bean is destroyed when the request ends.
Example: A login request creates a request‑scoped bean, which is discarded after the response; the next request gets a fresh bean.
Limitations: Only usable in web projects; non‑web projects throw errors. The main thread can use it, but asynchronous sub‑threads cannot by default.
Session
Explanation: The same bean instance is shared across multiple requests from the same browser session; it is destroyed when the browser closes or the session expires.
Example: After a user logs in, several API calls within an hour share the same session bean, suitable for storing temporary nickname or avatar information.
Note: Do not store large data, as it can consume server memory and cause Tomcat session overflow.
Application
Difference from Singleton: Singleton is per IoC container; Application scope is a global singleton across the entire Tomcat service, equivalent to a ServletContext global variable.
Typical use: Global configuration or application‑wide cache; most business beans never need this scope.
2. Common Pitfalls
Controller member variable causing data mixing
Symptom: Occasionally a user sees another user's shipping address; occurs a few times per day with no log errors.
@RestController
public class UserController {
// Wrong: mutable field in a singleton bean
private String userName;
@GetMapping("/info")
public Result getUser(String name) {
this.userName = name;
return userService.getInfo(userName);
}
}Root cause: Controllers are singleton; concurrent Tomcat threads overwrite the shared userName field, leading to data mixing.
Fixes:
Remove all member variables; use method‑local variables (stack‑based, thread‑isolated, zero overhead).
Annotate the controller with @Scope("prototype") to create a new instance per request.
Avoid adding synchronized; it drastically reduces throughput and can cause avalanche failures under high concurrency.
Prototype bean injected into a singleton service loses prototype behavior
Symptom: A tool class annotated with @Scope("prototype") is injected into a singleton service; under concurrency all threads receive the same instance, causing data interference.
Explanation: The singleton service is instantiated once at startup; Spring injects a single prototype instance at that time, and subsequent method calls reuse that same object, so the prototype annotation becomes ineffective.
@Service
public class TaskService {
// Lazy retrieval: get a new prototype each call
@Autowired
private ObjectProvider<TaskUtil> taskUtilProvider;
public void run() {
TaskUtil util = taskUtilProvider.getIfAvailable();
// use util
}
}This approach avoids manual ApplicationContext lookup, works with all Spring versions, and is production‑ready.
@Async call with request‑scoped bean throws NoSuchBeanException
Symptom: The main thread can obtain request parameters, but an asynchronous thread throws NoSuchBeanException.
Root cause: The request context is stored in a ThreadLocal of the Tomcat main thread; Spring’s default thread pool does not copy this context to async threads.
Solution: Use a custom async thread pool that propagates the request attributes, e.g.:
RequestContextHolder.setRequestAttributes(
RequestContextHolder.getRequestAttributes(), true);3. Scope Selection Guidance
Singleton
Thread‑safety: Stateless safe; stateful unsafe
Lifecycle: Project start → shutdown
Advice: Default for 95% of beans; avoid mutable fields
Prototype
Thread‑safety: Naturally thread‑safe
Lifecycle: Created on demand; no fixed destroy time
Advice: Use sparingly; excessive objects cause GC pressure
Request
Thread‑safety: Naturally thread‑safe
Lifecycle: One HTTP request
Advice: Store temporary request parameters
Session
Thread‑safety: Single‑user safe, multi‑user isolated
Lifecycle: User session lifetime
Advice: Light‑weight login info; avoid large data
Application
Thread‑safety: Thread‑unsafe
Lifecycle: Tomcat start → stop
Advice: Global static config; rarely needed
All scope‑related bugs appear only under concurrent load; single‑thread local tests cannot reveal them, leading to high remediation cost when they surface in production.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
