Spring Bean Scopes: Guide to understand the different Bean scopes
Introduction
This tutorial shows the different scopes you can assign to a Bean or a Component in Spring Framework.
It’s important to understand that a Bean definition is just a recipe to create instances of a class following the definition of this recipe. This recipe can be then used one or more times during the life cycle of the application to create an instance of the Bean.
The Bean scope is one of the main characteristics of the Bean configuration in Spring. The scope will indicate when and how is the object for the Bean definition going to be instantiated.
Spring includes 7 different Bean scopes:
- Singleton
- Prototype
- Request
- Session
- Global session
- Application
- Websocket
From these, only 5 of them are available in a web-aware application.
Singleton
Singleton is the default scope for a Bean, the one that will be used if nothing else is indicated. This scope implies that Spring container will create an only shared instance of the class designated by this bean, so each time the Bean is required the same object will be injected. The instance of this Bean is stored in a cache managed by Spring.
We can use annotations to define singletons as in the following example:
1@Bean(name = SINGLETON_BEAN_SAMPLE_NAME)
2public Sample sample() {
3 return new Sample();
4}
5
6@Bean(name = SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME)
7@Scope(SCOPE_SINGLETON)
8public Sample sampleAnnotated() {
9 return new Sample();
10}
As already stated, Singleton is the default scope for a Spring Bean, so it’s not necessary to define its @Scope
(first example). However, for better clarity and understanding, you can highlight this fact by using the @Scope
annotation as in the second example.
The following test shows the behavior of a singleton:
1@Test
2public void singletonTest_na_shouldBeSameInstance() {
3 Sample singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
4 Sample singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
5 Assert.assertEquals(singleton1, singleton2);
6 Sample singletonAnnotated1 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
7 Sample singletonAnnotated2 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
8 Assert.assertEquals(singletonAnnotated1, singletonAnnotated2);
9 Assert.assertNotEquals(singleton1, singletonAnnotated1);
10}
The behavior for @Component
(or any of its extensions) annotated classes is the same as for regular Beans, they will be singleton unless configured differently.
For example, a @RestController
as the following:
1@RestController
2public class SingletonScopedController extends AbstractController {
3 /* ... */
4 @GetMapping(AbstractController.SINGLETON_SCOPE_ENDPOINT)
5 public String getUuid() {
6 return super.getUuid();
7 }
8}
Will assert the following test:
1@Test
2public void singletonScopedController_na_shouldReturnSameValues() throws Exception {
3 String response1 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
4 .andReturn().getResponse().getContentAsString();
5 String response2 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
6 .andReturn().getResponse().getContentAsString();
7 Assert.assertEquals(response1, response2);
8}
Prototype
In the Prototype scope, Spring container will create a new instance of the object described by the bean each time it receives a request. One of the basic rules stated in Spring’s documentation is to use Prototype scoped beans when working on an environment where there is session management (Stateful) and use Singleton scope when working on environments without session management (Stateless), however there are many other more and different use cases.
It’s important to highlight that the Spring container doesn’t take responsibility for what happens to the instances of the objects received when requesting a bean. This means that it’s the developer’s task to perform clean up and free the resources referenced by the object defined by the Bean.
Same as with Singleton scope, we can define a bean with Prototype scope using annotations as follows::
1@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
2@Scope(SCOPE_PROTOTYPE)
3public Sample samplePrototype() {
4 return new Sample();
5}
The next test shows the behavior of a prototype:
1@Test
2public void prototypeTest_na_shouldBeDifferentInstance() {
3 Sample prototype1 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
4 Sample prototype2 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
5 Assert.assertNotEquals(prototype1, prototype2);
6}
Singleton Beans with injected Prototype Beans
It’s very important to highlight that when you define a Singleton Bean with dependencies or encapsulated Prototype Beans, injection happens when the Singleton object is instantiated so injection just happens once. Thus the injected object defined by the Prototype Bean will always be the same instance even though it was defined with a Prototype scope.
For example, we can define a class with an injected Bean as follows:
1public class SampleAutowired {
2
3 @Autowired
4 @Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
5 private Sample sampleAutowiredPrototype;
6
7 /* ... */
8}
We define the following Beans:
1@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
2@Scope(SCOPE_PROTOTYPE)
3@Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
4public Sample samplePrototype() {
5 return new Sample();
6}
7
8@Bean(name = SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME)
9@Scope(SCOPE_SINGLETON)
10public SampleAutowired sampleAutowiredPrototype() {
11 return new SampleAutowired();
12}
The first Bean (Sample
) has a Prototype scope and will be injected in the second (SampleAutowired
) which has a Singleton scope. As explained in the previous paragraphs, the expected behavior is that each time we request a SampleAutowired
, the instance of Sample will be the same even when it has a Prototype scope because it was instantiated and assigned only once when SampleAutowired
(Singleton) was first instantiated..
This is proved in the following test:
1@Test
2public void singletonWithAutowiredPrototype_na_shouldBeInstance() {
3 SampleAutowired singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
4 SampleAutowired singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
5 Assert.assertEquals(singleton1, singleton2);
6 Assert.assertEquals(singleton1.getSampleAutowiredPrototype(), singleton2.getSampleAutowiredPrototype());
7}
Web-aware application scopes
As mentioned in the introduction, the rest of the scopes are only available for web-aware applications.
Request
The Spring container will create a new instance of the object defined by the Bean each time it receives an HTTP request.
To define a Bean with this scope we can use the following annotations depending on if it’s a Bean or a Component:
1// Beans based on @Bean annotations
2@Bean
3@Scope(value = WebApplicationContext.SCOPE_REQUEST)
4public BeanSample beanSample() {
5 return new BeanSample ();
6}
7
8// Beans based on @Component annotations
9@RestController()
10@RequestScope
11public class RequestScopedController extends AbstractController{
12
13 @GetMapping(AbstractController.REQUEST_SCOPE_ENDPOINT)
14 public String getUuid() {
15 return super.getUuid();
16 }
17
18}
The behavior for the @RestController
in the previous snippet can be tested as follows:
1@Test
2public void requestScopedController_na_shouldReturnDifferentValues() throws Exception {
3 String response1 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
4 .andReturn().getResponse().getContentAsString();
5 String response2 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
6 .andReturn().getResponse().getContentAsString();
7 Assert.assertNotEquals(response1, response2);
8}
Session
Spring container will create a new instance of the object defined by the Bean for each of the HTTP sessions (Stateful application) and will return this same instance each time it receives a request within the same session.
1// Beans based on @Bean annotations
2@Bean
3@Scope(value = WebApplicationContext.SCOPE_SESSION)
4public BeanSample beanSample() {
5 return new BeanSample ();
6}
7
8//Beans based on @Component annotations
9@RestController
10@SessionScope
11public class SessionScopedController extends AbstractController {
12
13 @GetMapping(AbstractController.SESSION_SCOPE_ENDPOINT)
14 public String getUuid() {
15 return super.getUuid();
16 }
17}
The behavior for the @RestController
in the previous snippet can be tested as follows:
1@Test
2public void sessionScopedController_na_shouldReturnSameValues() throws Exception {
3 MockHttpSession session1 = new MockHttpSession();
4 MockHttpSession session2 = new MockHttpSession();
5 String response1_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
6 .andReturn().getResponse().getContentAsString();
7 String response1_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
8 .andReturn().getResponse().getContentAsString();
9 Assert.assertEquals(response1_1, response1_2);
10 String response2_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
11 .andReturn().getResponse().getContentAsString();
12 String response2_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
13 .andReturn().getResponse().getContentAsString();
14 Assert.assertEquals(response2_1, response2_2);
15 Assert.assertNotEquals(response1_1, response2_1);
16}
Global Session
The Global Session scope has a behavior similar to the Session scope but it’s only applicable in the context of Portlet based web applications. The Portlet specification defines a global session as a session that’s shared amongst al the Portlets that make up an application.
If this scope is used in a regular web application, the behavior of the Beans defined with this scope will be the same as if they had been defined with a Session scope.
1@Bean
2@Scope(value = WebApplicationContext.SCOPE_GLOBAL_SESSION)
3public BeanSample beanSample() {
4 return new BeanSample ();
5}
Application
The Application scope has a behavior very similar to the Singleton scope, Spring will create a new instance fo the bean for each ServletContext
. A Spring application can have several ServletContext, so the instance returned by Spring for each Bean request will be the same within the same context, but different for each of the contexts.
Websocket
The Spring container will create a new instance of the object defined by the Bean and return this instance for each request produced within the lifecycle of a Websocket.
Conclusion
This post can be used as a quick guide to understand the different scopes availabe in a Spring application. There are practical examples and unit tests to prove each of the behaviors. The full source code for this post can be found at Github.