I have a controller that I'd like to be unique per session. According to the spring documentation there are two details to the implementation:
1. Initial web configuration
To support the scoping of beans at the request, session, and global session levels (web-scoped beans), some minor initial configuration is required before you define your beans.
I've added the following to my web.xml
as shown in the documentation:
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
2. Scoped beans as dependencies
If you want to inject (for example) an HTTP request scoped bean into another bean, you must inject an AOP proxy in place of the scoped bean.
I've annotated the bean with @Scope
providing the proxyMode
as shown below:
@Controller
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class ReportBuilder implements Serializable {
...
...
}
Problem
In spite of the above configuration, I get the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.reportBuilder': Scope 'session' 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.
Update 1
Below is my component scan. I have the following in web.xml
:
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>org.example.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
And the following in AppConfig.java
:
@Configuration
@EnableAsync
@EnableCaching
@ComponentScan("org.example")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements AsyncConfigurer {
...
...
}
Update 2
I've created a reproducible test case. This is a much smaller project, so there are differences, but the same error happens. There's quite a few files, so I've uploaded it as a tar.gz
to megafileupload.
As per documentation:
If you are runnning outside of Spring MVC ( Not processed by DispatchServlet) you have to use the
RequestContextListener
Not justContextLoaderListener
.Add the following in your web.xml
That will provide session to Spring in order to maintain the beans in that scope
Update : As per other answers , the
@Controller
only sensible when you are with in Spring MVC Context, So the @Controller is not serving actual purpose in your code. Still you can inject your beans into any where with session scope / request scope ( you don't need Spring MVC / Controller to just inject beans in particular scope) .Update : RequestContextListener exposes the request to the current Thread only.
You have autowired ReportBuilder in two places
1.
ReportPage
- You can see Spring injected the Report builder properly here, because we are still in Same web Thread. i did changed the order of your code to make sure the ReportBuilder injected in ReportPage like this.i knew the log should go after as per your logic , just for debug purpose i added .
2.
UselessTasklet
- We got exception , here because this is different thread created by Spring Batch , where the Request is not exposed byRequestContextListener
.You should have different logic to create and inject
ReportBuilder
instance to Spring Batch ( May Spring Batch Parameters and usingFuture<ReportBuilder>
you can return for future reference)https://stackoverflow.com/a/30640097/2569475
For This Issue check My answer at above given url
Using a request scoped bean outside of an actual web request
My answer refers to a special case of the general problem the OP describes, but I'll add it just in case it helps somebody out.
When using
@EnableOAuth2Sso
, Spring puts anOAuth2RestTemplate
on the application context, and this component happens to assume thread-bound servlet-related stuff.My code has a scheduled async method that uses an autowired
RestTemplate
. This isn't running insideDispatcherServlet
, but Spring was injecting theOAuth2RestTemplate
, which produced the error the OP describes.The solution was to do name-based injection. In the Java config:
and in the class that uses it:
Now Spring injects the intended, servlet-free
RestTemplate
.If anyone else stuck on same point, following solved my problem.
In web.xml
In Session component
In pom.xml
I'm answering my own question because it provides a better overview of the cause and possible solutions. I've awarded the bonus to @Martin because he pin pointed the cause.
Cause
As suggested by @Martin the cause is the use of multiple threads. The request object is not available in these threads, as mentioned in the Spring Guide:
Solution 1
It is possible to make the request object available to other threads, but it places a couple of limitations on the system, which may not be workable in all projects. I got this solution from Accessing request scoped beans in a multi-threaded web application:
Solution 2
The only other option is to use the session scoped bean inside the request thread. In my case this wasn't possible because:
@Async
;You just need to define in your bean where you need a different scope than default singleton scope except prototype. For example: