2.4 Bean Scopes & Lifecycle
← Back to Chapter 2: Spring Beans & Dependency Injection
Understanding bean scoping rules and lifecycle interception hooks allows engineers to manage memory footprint, ensure thread safety, and implement robust resource allocation/teardown logic.
What is Bean scope?
Bean scope defines how many objects (instances) of a Spring Bean will be created and how long those objects will live.
In simple words:
Bean scope tells Spring whether to create one object, a new object every time, or an object for a particular request/session.
For example:
@Service
public class UserService {
}
Spring creates and manages this UserService object. The scope determines how Spring manages its instances.
1. Singleton Scope — Most Important
Singleton is the default scope in Spring.
It means:
Only one instance of the bean is created per Spring IoC container.
Example:
@Service
@Scope("singleton")
public class UserService {
}
Suppose multiple classes need UserService:
Controller ──┐
│
Service ─────┼──→ Same UserService object
│
Other Class ─┘
2. Prototype Scope
Spring creates a new bean instance every time the bean is requested from the container.
Example:
@Component
@Scope("prototype")
public class UserService {
}
Now:
UserService user1 = context.getBean(UserService.class);
UserService user2 = context.getBean(UserService.class);
Here:
user1 → UserService Object #1
user2 → UserService Object #2
Therefore:
user1 == user2
will be:
false
Simple difference
Singleton:
getBean() → Object #1
getBean() → Object #1
getBean() → Object #1
Prototype:
getBean() → Object #1
getBean() → Object #2
getBean() → Object #3
3. Request Scope
Request scope is mainly used in web applications.
It means:
A new bean instance is created for each HTTP request.
Example:
@Component
@Scope("request")
public class RequestData {
}
Suppose these requests come:
Request 1 → RequestData #1
Request 2 → RequestData #2
Request 3 → RequestData #3
Each HTTP request gets its own object.
4. Session Scope
Session scope means:
One bean instance is created for each HTTP session.
Example:
@Component
@Scope("session")
public class UserSession {
}
Suppose:
User A → Session A → UserSession #1
User B → Session B → UserSession #2
All requests from User A's session use the same UserSession object.
User A
│
├── Request 1 ──┐
├── Request 2 ──┼──→ UserSession #1
└── Request 3 ──┘
5. Application Scope
One bean instance exists for the lifecycle of the web application/ServletContext.
Example:
@Component
@Scope("application")
public class ApplicationData {
}
It is associated with the application's ServletContext.
You can think of it as application-wide data.
6. WebSocket Scope
WebSocket scope creates:
One bean instance for the lifecycle of a WebSocket session.
Example:
@Component
@Scope("websocket")
public class WebSocketData {
}
It's useful for applications that maintain WebSocket connections.
Spring Bean Scopes Matrix
Spring supports six core scopes (four of which are available only in web-aware contexts):
| Scope | Lifetime & Instance Count | Thread-Safety Considerations |
|---|---|---|
singleton (default) | Exactly one instance per Spring IoC container. Reused everywhere. | Must be stateless or thread-safe. Concurrent threads access same instance. |
prototype | A brand new instance is created every time the bean is requested. | Caller manages bean. Spring does not call @PreDestroy for prototype beans! |
request | One instance per HTTP request lifecycle. | Thread-safe per request thread. Destroyed when request completes. |
session | One instance per HTTP Session. | Scoped to specific user session. Serialized if sessions replicated. |
application | One instance per ServletContext. | Shared across all servlets in container. |
websocket | One instance per WebSocket lifecycle. | Bound to WebSocket session. |
What happens when Prototype is injected into Singleton?
When a prototype bean is injected into a singleton bean, the prototype bean is instantiated only once at container bootstrap! To receive a new prototype instance each time, use @Lookup or ObjectProvider<T>:
@Service
public class OrderService {
private final ObjectProvider<ReportGenerator> reportGeneratorProvider;
public OrderService(ObjectProvider<ReportGenerator> provider) {
this.reportGeneratorProvider = provider;
}
public void generate() {
ReportGenerator generator = reportGeneratorProvider.getObject(); // Fresh prototype instance!
generator.process();
}
}
Is Singleton Bean Thread-Safe?
No.
A singleton Bean means:
One instance is shared.
It does not automatically mean thread-safe.
Suppose:
@Service
public class CounterService {
private int count = 0;
public void increment() {
count++;
}
}
Multiple requests can access the same singleton instance concurrently.
Request 1 ──┐
Request 2 ──┼──→ Same CounterService instance
Request 3 ──┘
Complete Bean Lifecycle Hook Sequence
1. Instantiation (new MyBean())
│
2. Populate Properties (Dependency Injection)
│
3. BeanNameAware / BeanFactoryAware / ApplicationContextAware
│
4. BeanPostProcessor: postProcessBeforeInitialization()
│
5. @PostConstruct Method
│
6. InitializingBean: afterPropertiesSet()
│
7. Custom init-method (@Bean(initMethod = "..."))
│
8. BeanPostProcessor: postProcessAfterInitialization() (Proxying happens here!)
│
[ BEAN IS ACTIVE AND READY TO SERVE REQUESTS ]
│
9. @PreDestroy Method (Upon context shutdown)
│
10. DisposableBean: destroy()
│
11. Custom destroy-method (@Bean(destroyMethod = "..."))
@PostConstruct
@PostConstruct is used to run logic after dependency injection is complete and before the Bean is ready for normal use.
Example:
@Service
public class CustomerService {
@PostConstruct
public void initialize() {
System.out.println("CustomerService initialized");
}
}
Conceptually:
Create Bean
↓
Inject dependencies
↓
@PostConstruct
↓
Bean ready
It can be useful for initialization logic.
@PreDestroy
@PreDestroy is used for cleanup before a managed Bean is destroyed.
Example:
@Service
public class ConnectionService {
@PreDestroy
public void cleanup() {
System.out.println("Cleaning resources");
}
}
Conceptually:
Application shutting down
↓
@PreDestroy
↓
Bean destroyed
BeanPostProcessor Real-World Power
A BeanPostProcessor (BPP) can intercept and modify any bean instance before or after initialization:
@Component
public class CustomAuditPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean.getClass().isAnnotationPresent(Audited.class)) {
System.out.println("Intercepted bean for auditing: " + beanName);
// Can wrap bean in dynamic proxy here
}
return bean;
}
}
Spring itself uses BeanPostProcessors to implement @Async, @Transactional, and @Scheduled.