The Big Problem When Injecting Session Scope Into A Singleton: And How Spring’s Proxy Mode Solves It
1. Introduction: When You Need a "Personal" Bean in a "Shared" Application
In Spring Boot, Bean Scope is a fundamental concept, yet it often causes confusion as applications become more complex. A proper understanding of Scopes and their associated mechanisms is crucial for writing safe, performant code, and avoiding unexpected runtime errors.
We will focus on the two most common Scopes and their critical interaction: Singleton vs. Session.
2. The Default Scope: Singleton – The Application-Wide Bean
Singleton Scope is the default in Spring.
// Example: UserService is a Singleton (by default)
@Service
public class UserService {
// ...
}
3. The Specific Scope: Session – The Per-User Bean
Session Scope is a web scope that creates a new bean instance for each user's HTTP Session.
- User Context (login information, roles).
- Shopping Cart (temporary items).
- Authentication detail.
@Component
@Scope(value = "session")
public class UserSessionContext {
private String username;
// ...
}
4. The Big Problem: How to Inject a Session Bean into a Singleton?
This is a common and tricky situation:
Singleton Bean <--> Session Bean
The code that causes a startup failure (without Proxy Mode):
@Service // Singleton
public class UserCartService {
@Autowired // Startup Fails!
private UserSessionContext sessionContext; // Session Scope
// ...
}
5. The "Magic" Solution: Proxy Mode
To resolve this conflict, Spring provides the Proxy Mode mechanism. Instead of injecting the REAL Session Bean into the Singleton, Spring injects a Proxy Bean (a stand-in)!
You only need to add the proxyMode attribute to the @Scope annotation:
@Component
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS) // <--- THE KEY
public class UserSessionContext {
private String username;
// ...
}
How Proxy Mode Works:
2. At Runtime (Method Call): When the UserCartService calls a method on the Proxy (e.g., sessionContext.getUsername()), the Proxy takes over:
3. Result: Every request (from every user session) calling the Singleton Service will always receive the Session Bean appropriate for their user!
The takeaway: The Singleton holds the Proxy, and the Proxy is responsible for finding the correct Session Bean at the right time and place.
Flow Explanation:
2. Runtime Interaction (The Call):
3. Proxy Lookup (The Magic):
4. SessionScope Delegation:
Example:
Conclusion
Using proxyMode=ScopedProxyMode.TARGET_CLASS is the correct answer when you need to inject a narrower-scoped bean (like session, request) into a wider-scoped bean (like singleton). It ensures you always interact with the bean instance appropriate for the current context (session, request) without violating Spring's creation lifecycle rules.