Spring Bean Scope — How Many Copies?
A developer adds a field private int borrowCount; to LoanService to count one member's borrowings, and in testing it works. In production, members start seeing each other's counts. Nothing is wrong with the arithmetic. The bug is about how many LoanService objects exist, and that is what a bean's scope decides.
The rule
Singleton by default: one shared bean, so keep it stateless.
A bean's scope says how many instances the container creates and how long each lives.
| Scope | Instances | Available in |
|---|---|---|
singleton (default) |
One per application context, shared by everyone | Any app |
prototype |
A new one each time the bean is requested from the container | Any app |
request |
One per HTTP request | Web apps |
session |
One per HTTP session | Web apps |
application |
One per servlet context | Web apps |
@Component
@Scope("prototype") // or @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LoanReceiptBuilder {
private final List<String> lines = new ArrayList<>();
public void add(String line) { lines.add(line); }
}Because a singleton is shared by every request, and a web server handles many requests at once on different threads, a singleton must not hold per-user or per-request data in its fields. Fields that hold collaborators (a repository, the settings) are fine because they never change after startup. That was the borrowCount bug: one field, one object, every member writing to it.
Almost every bean you write — controllers, services, repositories — should be a stateless singleton. Keep changing data in local variables, method parameters, or the database.
The shortcut
For every field in a bean, ask: "Does this value change while the app runs?"
- No (injected collaborators, settings): safe in a singleton.
- Yes: move it into a local variable or the database, or, rarely, give the bean a narrower scope.
Find the field
LoanServicehasprivate int borrowCount;, updated on every borrow.
A second trap: injecting a prototype bean into a singleton. The singleton is built once, so it receives one prototype instance at startup and keeps it forever: you get a shared object after all. When you truly need a fresh instance per use, inject an ObjectProvider<LoanReceiptBuilder> and call getObject() each time.
Drills
Answer fast — trust the rule.
What is the default scope of a Spring bean?
Which LoanService field is safe in a singleton?
Which scope creates a new instance every time the bean is requested from the container?
A singleton LoanService injects a prototype LoanReceiptBuilder in its constructor. How many builders does LoanService use across 1,000 borrow calls?
Which scope gives each HTTP request its own instance?
Remember
- Singleton by default: one shared bean, so keep it stateless.
- For each field, ask "does it change at runtime?" If yes, it does not belong in a singleton.
- Trap: a prototype injected into a singleton is created only once — use
ObjectProviderfor a fresh one.
