Bean scopes and why state is a bug
One singleton serves every request thread, so a mutable field leaks data between users.
Open this lesson in the learning hubKey points
- The default scope is singleton: one instance for the whole context, shared by every request thread.
- That is safe only while the bean is stateless. A mutable field is shared mutable state under load.
- Keep per-request data in parameters and local variables - those live on the calling thread stack.
prototypehands out a new instance per lookup, but a singleton that injects one still keeps the same copy.- For genuine per-request state use
@RequestScope; Spring injects a proxy that resolves per request. - Never park the logged-in user in a field. Take it as an argument or read the security context.
Example
@Service
class BrokenOrderService { // singleton: ONE instance for the whole app
private String currentUser; // shared by every request thread
List<Order> forUser(String user) {
this.currentUser = user; // thread B overwrites this...
List<Order> found = repo.findByUser(this.currentUser);
return filter(found, this.currentUser); // ...before thread A reads it
}
}
@Service
class GoodOrderService { // still a singleton, but stateless
List<Order> forUser(String user) {
return filter(repo.findByUser(user), user); // no field, nothing shared
}
}
@Component
@RequestScope // genuinely per request, injected as a proxy
class RequestContext {
private String traceId; // getters and setters omitted
}
Singleton is the right default, and it only works because you keep the bean stateless.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Boot course, and every lesson in it is listed on the Spring Boot contents page.