Bean scopes in Spring

Spring beans, like the ones from JavaBeans, use the scopes. We've already seen two of them, singleton and prototype. It's the right time to discover 3 new scopes.

Looking for a better data engineering position and skills?

You have been working as a data engineer but feel stuck? You don't have any new challenges and are still writing the same jobs all over again? You have now different options. You can try to look for a new job, now or later, or learn from the others! "Become a Better Data Engineer" initiative is one of these places where you can find online learning resources where the theory meets the practice. They will help you prepare maybe for the next job, or at least, improve your current skillset without looking for something else.

👉 I'm interested in improving my data engineering skillset

See you there, Bartosz

This article will be divided in 2 parts. Each part will describe a new bean scope. So, in the first one, we'll discover the request scope. The second described scope will be session and global session scope. All parts will be composed by a theoretical introduction and real life use. These new concepts are only valid in web-aware Spring's application context.

What is request scope in Spring ?

Bean annotated with this scope is initialized at every request. It sounds like a description of prototype scope but they are some differences. The first difference is that prototype scope is available globally in Spring's context. The request one is only available for web applications. The second one is that prototype beans are initialized at demand whereas request beans are constructed at every request, event without explicit desire of programmer. In plus, they are one and exactly one instance of request scoped bean. In the other side, you can have one or more instance of prototype scoped bean.

In the following code you can see the example of request scope bean:

<bean id="shoppingCartRequest" class="com.waitingforcode.scope.ShoppingCartRequest" scope="request">
	<aop:scoped-proxy/> 
</bean>
// request bean

// injection sample
@Controller
public class TestController {
	@Autowired
	private ShoppingCartRequest shoppingCartRequest;
	
	@RequestMapping(value = "/test", method = RequestMethod.GET)
	public String test(HttpServletRequest request) {
		LOGGER.debug("shoppingCartRequest is :"+shoppingCartRequest);
		// ...
	}
}

Please note the presence of <aop:scoped-proxy /> tag. It means the use of proxy objects. So in reality, TestControllers holds a reference to proxy object. This object forwards after all calls to the real ShoppingCartRequest object.

Sometimes we need to use another servlet that DispatcherServlet to treat the request. In this situation we must be sure that all request available for Spring (otherwise an exception similar to below can be thrown). To make that, you can define a listener in web.xml:

<listener>   
  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

After calling /test URL, you should retrieve following entries in the logs:

shoppingCartRequest is :com.waitingforcode.scope.ShoppingCartRequest@2586b11c
shoppingCartRequest is :com.waitingforcode.scope.ShoppingCartRequest@3bd5b945

If we try to use request scoped bean inside singleton bean, a BeanCreationException can be thrown at application context loading stage:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.waitingforcode.scope.ShoppingCartRequest com.waitingforcode.controller.TestController.shoppingCartRequest; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shoppingCartRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
	at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:381)
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:293)
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4701)
	at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5204)
	at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5199)
	at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
	at java.util.concurrent.FutureTask.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.waitingforcode.scope.ShoppingCartRequest com.waitingforcode.controller.TestController.shoppingCartRequest; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shoppingCartRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
	... 21 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shoppingCartRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:353)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1014)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:957)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:855)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
	... 23 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
	at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
	at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:41)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
	... 28 more

What is session scope in Spring ?

Session-scoped beans are not so different from the request ones. They are also associated with purely web application context. Beans annotated as session-scoped are created only once for every user's session. They are destructed at the session ending.

Beans limited by session scope can be considered as web-oriented singletons because only one instance is present for a given environment (user's session). But remember that you couldn't use them outside web application context.

To see session-scoped beans in action, let's define one in configuration file:

<bean id="shoppingCartRequest" class="com.waitingforcode.scope.ShoppingCartSession" scope="session">
	<aop:scoped-proxy/> 
</bean>

The way of retrieving it is the same as for request-scoped bean, with help of @Autowired annotation. We put exactly the same entry to the logs. It's done the following results:

shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@2f87fafc

As you can see, the first 5 prints represent the same object. The last one is different. What does it mean ? Simply, it means a new user who accessed the page with autowired session-scoped bean. We can observe it by opening the test page (/test) with two different browsers. Each will initialize a new session, and so create new ShoppingCartSession bean instance.

Regarding global session scope, it's reserved to portlet applications. Already confused ? So, let's explain shortly what the portlets are. Portlets are small Java web plugins able to generate semantic code (for example: HTML) snippets. They are based on portlet container and can process HTTP request exactly as the servlets do. But unlike the servlets, each portlet has different session. In this case, Spring provides a scope called global-session. Thanks to it, one bean can be shared through multiple portlets in the application.

This article added some of explanations for already approached scopes, singleton and prototype. Here, we discovered request and session-oriented scopes. The first one causes that new bean is created on every request. The second one initializes the bean at the same moment as the session. After the same bean's instance is available for whole session lifecycle.


If you liked it, you should read:

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!