A logo showing the text blog.marcnuri.com
Español
Home»Backend Development»Spring Bean Scopes: Guide to understand the different Bean scopes

Recent Posts

  • Eclipse JKube 1.19 is now available!
  • 2025 Year in Review: The Year of AI
  • Synology DS224+: How to upgrade hard drives in RAID 1
  • Fabric8 Kubernetes Client 7.5 is now available!
  • Boosting My Developer Productivity with AI in 2025

Categories

  • Artificial Intelligence
  • Backend Development
  • Cloud Native
  • Engineering Insights
  • Frontend Development
  • JavaScript
  • Legacy
  • Operations
  • Personal
  • Pet projects
  • Quality Engineering
  • Tools

Archives

  • February 2026
  • January 2026
  • December 2025
  • October 2025
  • September 2025
  • July 2025
  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • July 2019
  • March 2019
  • November 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • December 2017
  • October 2017
  • August 2017
  • July 2017
  • January 2017
  • December 2015
  • November 2015
  • December 2014
  • November 2014
  • October 2014
  • March 2014
  • February 2011
  • November 2008
  • June 2008
  • May 2008
  • April 2008
  • January 2008
  • November 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007

Spring Bean Scopes: Guide to understand the different Bean scopes

2018-04-30 in Backend Development tagged Bean / Java / Scopes / Spring Framework / Spring Boot / Testing by Marc Nuri | Last updated: 2026-02-16
Versión en Español

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:

ScopeLifetimeUse case
SingletonApplicationStateless services, shared resources
PrototypePer request to containerStateful objects, builders
RequestHTTP requestRequest-specific data
SessionHTTP sessionUser session data
ApplicationServletContextApp-wide shared state
WebSocketWebSocket sessionWebSocket-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:

SingletonScopedController.java
@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:

ScopeInstancesUse Case
SingletonOne per containerStateless services, shared resources
PrototypeNew per requestStateful beans, short-lived objects
RequestOne per HTTP requestRequest-specific data
SessionOne per HTTP sessionUser session data
ApplicationOne per ServletContextApplication-wide resources
WebSocketOne per WebSocket sessionWebSocket-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

  • Spring Framework Reference - Bean Scopes
Scopes in a Spring Bean application
Scopes in a Spring Bean application
Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Post navigation
Spring Bean Scopes: Singleton with PrototypesJava 10: Testing the new release
© 2007 - 2026 Marc Nuri