How does a session-scoped bean behave across multiple requests and in a clustered/replicated environment?
answer
- one instance per HttpSession, stored as session attribute
- @PreDestroy on session invalidate/timeout, not per request
- must be Serializable for replication
- parallel tab requests → guard mutable state
- Spring Session / Redis externalization
basics
~20 sA session-scoped bean has one instance per HttpSession, shared across that user's requests until the session ends. If sessions are replicated across a cluster, the bean must be Serializable and mutations may not propagate instantly, so avoid non-serializable or heavily mutable state.
solid answer
~50 s@SessionScope gives one bean instance per HttpSession, stored as a session attribute and shared across all requests within that session; it's destroyed when the session is invalidated or times out (its @PreDestroy runs then). Injected into singletons it needs a scoped proxy so each request routes to the right session's instance. In a clustered setup with session replication (e.g., Spring Session + Redis, or container replication), the bean is serialized into the session store, so it must implement Serializable and every field must be serializable too. Watch for staleness: with sticky sessions it's fine, but with replication a mutation on one node may lag on another; store minimal, serializable, ideally immutable state, and treat the store as the source of truth. Concurrency also matters: parallel requests in one session can hit the same instance, so guard mutable state.
code
java · 11 lines@Component
@SessionScope // TARGET_CLASS proxy by default
public class UserPreferences implements Serializable {
private static final long serialVersionUID = 1L;
private volatile String theme = "dark"; // volatile: parallel requests
public String getTheme() { return theme; }
public void setTheme(String t) { this.theme = t; }
@PreDestroy
void onSessionEnd() { /* runs on session invalidate/timeout */ }
}go deeper
Know it's one instance per user session shared across their requests.
Explain the scoped proxy for injecting into singletons and destruction on session end.
Discuss Serializable/replication, staleness with sticky vs replicated sessions, and concurrency.
Design session-state strategy: minimal immutable state, externalized store as source of truth, replication-cost tradeoffs, alternatives (tokens/DB) to session state.
## Session scope semantics `@SessionScope` (or `@Scope("session")`) binds one bean instance to a single `HttpSession`. The instance is stored as a **session attribute** and is returned for every request belonging to that session, so it naturally carries per-user state (preferences, a wizard's progress, a cart). Its full lifecycle is managed: initialization callbacks run when first created within a session; **destruction callbacks** (`@PreDestroy`, `DisposableBean`) run when the session is **invalidated** or **times out** — not when an individual request ends. ## Injection into singletons Because a singleton is wired once, a session-scoped dependency needs `proxyMode = TARGET_CLASS` (the default of `@SessionScope`). The injected proxy resolves the current thread's session (via `RequestContextHolder` → `HttpSession`) on each call, so different users transparently get different backing instances through the *same* proxy. ## Serialization & clustering Sessions are often **persisted or replicated**: - Container-level replication (e.g., Tomcat clustering) serializes session attributes across nodes. - **Spring Session** externalizes the session to Redis/JDBC/Hazelcast. In both cases the session-scoped bean is serialized, so: - The class **must implement `java.io.Serializable`** and all retained fields must be serializable (or `transient` and rebuildable). - Non-serializable dependencies (e.g., a DataSource) should not be stored as instance state; inject collaborators as proxies/providers instead of caching them. ## Staleness & consistency - **Sticky sessions**: all of a user's requests hit one node — no replication lag, simplest. - **Replicated / externalized sessions**: a write on node A may not be visible on node B until the session is re-serialized and propagated; treat the store as source of truth, keep the bean small, prefer immutable snapshots, and avoid write-heavy per-request mutation. ## Concurrency within a session Browsers issue **parallel** requests (tabs, prefetch), and they can execute on different threads while sharing the same session instance. Mutable session-scoped state therefore needs thread-safety (synchronization or immutable/atomic fields). Spring does not serialize access to a session-scoped bean for you. ## Gotchas - Forgetting `Serializable` → `NotSerializableException` on replication/passivation. - Expecting `@PreDestroy` per request — it fires on session end, not request end. - Storing large graphs in session bloats the session store and replication traffic. - WebSocket/STOMP has its own `websocket` scope; don't conflate it with HTTP session scope.
- When does a session-scoped bean's @PreDestroy run?When the HttpSession is invalidated or times out — not at the end of each request. Spring registers a session-attribute-based destruction callback tied to the session lifecycle.
- Why might a session-scoped bean need to be Serializable?If the session is passivated to disk or replicated/externalized across a cluster (container clustering or Spring Session with Redis/JDBC), its attributes are serialized, so the bean and its retained fields must be serializable.
saying these in an interview costs you the question
- Assuming session beans are automatically thread-safe across parallel requests
- Expecting @PreDestroy to fire at the end of every request
- Storing non-serializable state and being surprised by NotSerializableException in a cluster