当前线程的范围“会话"未激活;IllegalStateException:找不到线程绑定请求 [英] Scope 'session' is not active for the current thread; IllegalStateException: No thread-bound request found

查看:23
本文介绍了当前线程的范围“会话"未激活;IllegalStateException:找不到线程绑定请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个控制器,我希望每个会话都是唯一的.根据 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.初始网络配置

为了在请求、会话和全局会话级别(网络范围的 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.作用域 bean 作为依赖项

如果您想将(例如)一个 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 时出错:当前线程的作用域会话"未激活;如果您打算从单例中引用它,请考虑为此 bean 定义范围代理;嵌套异常是 java.lang.IllegalStateException: No thread-bound request found: 您是指实际 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 上传到 megafileupload.

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.

原因

正如@Martin 所建议的,原因是使用了多个线程.请求对象在这些线程中不可用,如 Spring 指南:

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:

DispatcherServletRequestContextListenerRequestContextFilter 都做完全相同的事情,即将 HTTP 请求对象绑定到为该请求提供服务的线程.这使得请求和会话范围内的 bean 在调用链的更下游可用.

DispatcherServlet, RequestContextListener and RequestContextFilter 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

可以使请求对象对其他线程可用,但它对系统施加了一些限制,这可能不适用于所有项目.我从 Accessing request scoped beans in multi-线程网络应用:

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 of WorkManagerTaskExecutor / ThreadPoolExecutorFactoryBean. The benefit is that SimpleAsyncTaskExecutor will never re-use threads. That's only half the solution. The other half of the solution is to use a RequestContextFilter instead of RequestContextListener. RequestContextFilter (as well as DispatcherServlet) has a threadContextInheritable 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:

  1. 控制器方法用@Async注解;
  2. 控制器方法启动一个批处理作业,该作业使用线程进行并行作业步骤.

这篇关于当前线程的范围“会话"未激活;IllegalStateException:找不到线程绑定请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆