Spring Bean Scopes: Guide to understand the different Bean scopes
Introduction
In Spring Framework, a bean definition is essentially a recipe for creating object instances. The scope determines when and how Spring instantiates these objects: should it create one instance for the entire application, or a new one for each request?
Spring provides six bean scopes:
| Scope | Lifetime | Use case |
|---|---|---|
| Singleton | Application | Stateless services, shared resources |
| Prototype | Per request to container | Stateful objects, builders |
| Request | HTTP request | Request-specific data |
| Session | HTTP session | User session data |
| Application | ServletContext | App-wide shared state |
| WebSocket | WebSocket session | WebSocket-specific data |
The last four (Request, Session, Application, WebSocket) require a web-aware ApplicationContext and won't work in standalone applications.
Singleton scope
Singleton is the default scope. Spring creates exactly one instance of the bean per container and caches it for all subsequent requests. Every time you request this bean, you get the same object.
You can define singletons using annotations:
@Bean(name = SINGLETON_BEAN_SAMPLE_NAME)
public Sample sample() {
return new Sample();
}
@Bean(name = SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME)
@Scope(SCOPE_SINGLETON)
public Sample sampleAnnotated() {
return new Sample();
}Since Singleton is the default, the @Scope annotation is optional (first example).
I recommend adding it explicitly (second example) to make your intent clear to other developers.
The following test demonstrates singleton behavior:
@Test
public void singletonTest_na_shouldBeSameInstance() {
Sample singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
Sample singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
Assert.assertEquals(singleton1, singleton2);
Sample singletonAnnotated1 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
Sample singletonAnnotated2 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
Assert.assertEquals(singletonAnnotated1, singletonAnnotated2);
Assert.assertNotEquals(singleton1, singletonAnnotated1);
}Classes annotated with @Component (or its specializations like @Service, @Repository, @Controller) follow the same rules: singleton by default.
Here's a @RestController example:
@RestController
public class SingletonScopedController extends AbstractController {
/* ... */
@GetMapping(AbstractController.SINGLETON_SCOPE_ENDPOINT)
public String getUuid() {
return super.getUuid();
}
}This test using MockMvc verifies the singleton behavior:
@Test
public void singletonScopedController_na_shouldReturnSameValues() throws Exception {
String response1 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
.andReturn().getResponse().getContentAsString();
String response2 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
.andReturn().getResponse().getContentAsString();
Assert.assertEquals(response1, response2);
}Prototype scope
With Prototype scope, Spring creates a new instance every time you request the bean from the container. This is useful for stateful objects that shouldn't be shared, like builders or objects with mutable state.
Warning
Spring doesn't manage the full lifecycle of prototype beans. Once the container hands you the instance, you're responsible for cleanup and releasing resources.
Define a prototype bean using annotations:
@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
@Scope(SCOPE_PROTOTYPE)
public Sample samplePrototype() {
return new Sample();
}This test shows that each request returns a different instance:
@Test
public void prototypeTest_na_shouldBeDifferentInstance() {
Sample prototype1 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
Sample prototype2 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
Assert.assertNotEquals(prototype1, prototype2);
}The prototype-in-singleton problem
Here's a common gotcha: when you inject a Prototype bean into a Singleton, the injection happens only once (when the Singleton is created). This means your "prototype" effectively behaves like a singleton.
Consider this class with an injected dependency:
public class SampleAutowired {
@Autowired
@Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
private Sample sampleAutowiredPrototype;
/* ... */
}With these bean definitions:
@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
@Scope(SCOPE_PROTOTYPE)
@Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
public Sample samplePrototype() {
return new Sample();
}
@Bean(name = SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME)
@Scope(SCOPE_SINGLETON)
public SampleAutowired sampleAutowiredPrototype() {
return new SampleAutowired();
}The Sample bean has Prototype scope, but since SampleAutowired is a Singleton, Spring injects Sample only once during instantiation.
Every subsequent request for SampleAutowired returns the same object with the same Sample instance, defeating the purpose of Prototype scope.
The PrototypeInSingleton example demonstrates this:
@Test
public void singletonWithAutowiredPrototype_na_shouldBeInstance() {
SampleAutowired singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
SampleAutowired singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
Assert.assertEquals(singleton1, singleton2);
Assert.assertEquals(singleton1.getSampleAutowiredPrototype(), singleton2.getSampleAutowiredPrototype());
}Tip
For solutions to this problem using ObjectProvider, @Lookup, or scoped proxies, see my follow-up post: Spring Bean Scopes: Singleton with Prototypes.
Web-aware scopes
The remaining scopes (Request, Session, Application, WebSocket) only work in web applications with a WebApplicationContext.
Request scope
Spring creates a new instance for each HTTP request. The bean lives only for the duration of that request and is destroyed when the response is sent.
Define a request-scoped bean:
// Beans based on @Bean annotations
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public BeanSample beanSample() {
return new BeanSample ();
}
// Beans based on @Component annotations
@RestController()
@RequestScope
public class RequestScopedController extends AbstractController{
@GetMapping(AbstractController.REQUEST_SCOPE_ENDPOINT)
public String getUuid() {
return super.getUuid();
}
}Each request gets a fresh instance:
@Test
public void requestScopedController_na_shouldReturnDifferentValues() throws Exception {
String response1 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
.andReturn().getResponse().getContentAsString();
String response2 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
.andReturn().getResponse().getContentAsString();
Assert.assertNotEquals(response1, response2);
}Session scope
Spring creates one instance per HTTP session. All requests within the same session share the same bean instance, making it perfect for user-specific data like shopping carts or preferences.
// Beans based on @Bean annotations
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION)
public BeanSample beanSample() {
return new BeanSample ();
}
//Beans based on @Component annotations
@RestController
@SessionScope
public class SessionScopedController extends AbstractController {
@GetMapping(AbstractController.SESSION_SCOPE_ENDPOINT)
public String getUuid() {
return super.getUuid();
}
}This test shows same-session requests share an instance, but different sessions get different instances:
@Test
public void sessionScopedController_na_shouldReturnSameValues() throws Exception {
MockHttpSession session1 = new MockHttpSession();
MockHttpSession session2 = new MockHttpSession();
String response1_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
.andReturn().getResponse().getContentAsString();
String response1_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
.andReturn().getResponse().getContentAsString();
Assert.assertEquals(response1_1, response1_2);
String response2_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
.andReturn().getResponse().getContentAsString();
String response2_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
.andReturn().getResponse().getContentAsString();
Assert.assertEquals(response2_1, response2_2);
Assert.assertNotEquals(response1_1, response2_1);
}Application scope
Application scope creates one instance per ServletContext.
This is similar to Singleton, but the distinction matters when multiple servlet-based applications share the same ServletContext: they'll share the same bean instance.
For most Spring Boot applications with a single ServletContext, Application scope behaves identically to Singleton.
WebSocket scope
Spring creates one instance per WebSocket session. The bean lives for the duration of the WebSocket connection and is shared across all messages within that session.
Thread scope
Spring provides SimpleThreadScope for per-thread bean instances, but it's not registered by default.
Register it manually if needed:
@Configuration
public class ThreadScopeConfig {
@Bean
public static CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("thread", new SimpleThreadScope());
return configurer;
}
}Note
Spring 5.0 removed the Global Session scope that was used for Portlet-based applications. If you're working with legacy Portlet code, this scope is no longer available.
Frequently asked questions
What's the default bean scope in Spring?
Singleton. If you don't specify a scope, Spring creates one instance per container and reuses it for all requests.
When should I use Prototype instead of Singleton?
Use Prototype when each consumer needs its own instance with independent state. Common examples include builders, command objects, or any bean that maintains mutable state that shouldn't be shared.
Can I inject a Prototype bean into a Singleton?
Yes, but with a catch: the Prototype gets injected once when the Singleton is created.
For true Prototype behavior, use ObjectProvider, @Lookup, or scoped proxies.
See Spring Bean Scopes: Singleton with Prototypes for detailed solutions.
What's the difference between Singleton and Application scope?
In most Spring Boot apps, they're identical.
The difference appears when multiple servlet-based applications share the same ServletContext: Application-scoped beans are shared across all of them, while Singleton beans are scoped to a single application context.
Conclusion
Spring's bean scopes control when and how objects are instantiated. The right scope depends on your use case:
| Scope | Instances | Use Case |
|---|---|---|
| Singleton | One per container | Stateless services, shared resources |
| Prototype | New per request | Stateful beans, short-lived objects |
| Request | One per HTTP request | Request-specific data |
| Session | One per HTTP session | User session data |
| Application | One per ServletContext | Application-wide resources |
| WebSocket | One per WebSocket session | WebSocket-specific data |
For more on dependency injection patterns and testing Spring components, check out my other tutorials.
The full source code with runnable JBang examples is available on GitHub.
References

