作用域“会话"在当前线程中不起作用; IllegalStateException:找不到线程绑定的请求 [英] Scope 'session' is not active for the current thread; IllegalStateException: No thread-bound request found
问题描述
我有一个控制器,希望每个会话都唯一.根据spring文档,实现有两个细节:
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.初始Web配置
要在请求,会话和全局会话级别(网络范围的Bean)上支持Bean的作用域,在定义Bean之前,需要一些较小的初始配置.
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.
我已将以下内容添加到我的web.xml
中,如文档所示:
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.范围豆作为依赖项
如果要将(例如)HTTP请求范围的bean注入另一个bean,则必须注入AOP代理来代替范围的bean.
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.
我用@Scope
注释了bean,提供了proxyMode
,如下所示:
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 {
...
...
}
问题
尽管进行了上述配置,我还是收到以下异常:
In spite of the above configuration, I get the following exception:
org.springframework.beans.factory.BeanCreationException:创建名称为'scopedTarget.reportBuilder'的bean时出错:当前线程的作用域'session'无效;如果您打算从单例中引用它,请考虑为此bean定义作用域代理.嵌套异常为java.lang.IllegalStateException:未找到线程绑定请求:您是在实际Web请求之外引用请求属性,还是在原始接收线程之外处理请求?如果您实际上是在Web请求中操作并且仍然收到此消息,则您的代码可能在DispatcherServlet/DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter公开当前请求.
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.
更新1
下面是我的组件扫描.我在web.xml
中有以下内容:
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>
以及AppConfig.java
中的以下内容:
@Configuration
@EnableAsync
@EnableCaching
@ComponentScan("org.example")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements AsyncConfigurer {
...
...
}
更新2
我创建了一个可复制的测试用例.这是一个较小的项目,因此存在差异,但是会发生相同的错误.有很多文件,因此我已将其作为tar.gz
格式上传到
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.
推荐答案
我正在回答自己的问题,因为它可以更好地概述原因并提供可能的解决方案.我已将奖金授予@Martin,因为他指出了原因.
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.
原因
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:
DispatcherServlet
,RequestContextListener
和RequestContextFilter
都做完全相同的事情,即将HTTP请求对象绑定到为该请求提供服务的线程.这样一来,请求和会话范围内的bean就可以在调用链的下游使用.
DispatcherServlet
,RequestContextListener
andRequestContextFilter
all do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.
解决方案1
可以使请求对象可用于其他线程,但是它对系统有一些限制,可能并非在所有项目中都可行.我从>在多个线程的Web应用程序:
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:
我设法解决了这个问题.我开始使用
SimpleAsyncTaskExecutor
而不是WorkManagerTaskExecutor
/ThreadPoolExecutorFactoryBean
.好处是SimpleAsyncTaskExecutor
将永远不会重用线程.那只是解决方案的一半.解决方案的另一半是使用RequestContextFilter
而不是RequestContextListener
.RequestContextFilter
(以及DispatcherServlet
)具有threadContextInheritable
属性,该属性基本上允许子线程继承父上下文.
I managed to get around this issue. I started using
SimpleAsyncTaskExecutor
instead ofWorkManagerTaskExecutor
/ThreadPoolExecutorFactoryBean
. The benefit is thatSimpleAsyncTaskExecutor
will never re-use threads. That's only half the solution. The other half of the solution is to use aRequestContextFilter
instead ofRequestContextListener
.RequestContextFilter
(as well asDispatcherServlet
) has athreadContextInheritable
property which basically allows child threads to inherit the parent context.
解决方案2
唯一的其他选择是在请求线程内使用会话范围的Bean.就我而言,这是不可能的,因为:
The only other option is to use the session scoped bean inside the request thread. In my case this wasn't possible because:
- 控制器方法带有
@Async
注释; - 控制器方法将启动批处理作业,该批处理作业将线程用于并行作业步骤.
- The controller method is annotated with
@Async
; - The controller method starts a batch job which uses threads for parallel job steps.
这篇关于作用域“会话"在当前线程中不起作用; IllegalStateException:找不到线程绑定的请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!