猿问

作用域“会话”对当前线程无效;IllegalStateException:找不到线程绑定的请求

我有一个控制器,希望每个会话都唯一。根据spring文档,实现有两个细节:


1.初始Web配置


为了在请求,会话和全局会话级别(网络范围的Bean)支持Bean的作用域,在定义Bean之前,需要一些较小的初始配置。


web.xml如文档所示,我已经添加了以下内容:


<listener>

  <listener-class>

    org.springframework.web.context.request.RequestContextListener

  </listener-class>

</listener>

2.范围豆作为依赖项


如果要将(例如)HTTP请求范围的bean注入另一个bean,则必须注入AOP代理来代替范围的bean。


我通过@Scope提供proxyMode如下所示对bean进行了注释:


@Controller

@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)

public class ReportBuilder implements Serializable {

    ...

    ...

}

问题


尽管进行了上述配置,我还是收到以下异常:


org.springframework.beans.factory.BeanCreationException:创建名称为“ scopedTarget.reportBuilder”的bean时出错:当前线程的作用域“会话”未激活;如果您打算从单例中引用它,请考虑为此bean定义作用域代理。嵌套异常为java.lang.IllegalStateException:未找到线程绑定的请求:您是在实际Web请求之外引用请求属性,还是在原始接收线程之外处理请求?如果您实际上是在Web请求中操作并且仍然收到此消息,则您的代码可能在DispatcherServlet / DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter公开当前请求。


更新1


以下是我的组件扫描。我有以下内容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。


MM们
浏览 855回答 3
3回答

qq_遁去的一_1

问题不在于您的Spring注释,而在于您的设计模式。您将不同的作用域和线程混合在一起:单身人士会话(或请求)作业线程池单身人士可以在任何地方使用,没关系。但是,会话/请求范围在附加到请求的线程之外不可用。即使请求或会话不再存在,异步作业也可以运行,因此无法使用依赖于请求/会话的bean。同样也没有办法知道,如果您正在另一个线程中运行作业,哪个线程是发起者请求(这意味着aop:proxy在这种情况下没有帮助)。我觉得你的代码看起来像要作合同&nbsp;ReportController,报表制作,UselessTask和ReportPage之间。有没有办法只使用一个简单的类(POJO)来存储UselessTask中的数据并在ReportController或ReportPage中读取它,而不再使用ReportBuilder?

largeQ

如果还有其他人坚持同一观点,以下解决了我的问题。在web.xml中&nbsp;<listener>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <listener-class>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; org.springframework.web.context.request.RequestContextListener&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </listener-class>&nbsp; </listener>会话中组件@Component@Scope(value = "session",&nbsp; proxyMode = ScopedProxyMode.TARGET_CLASS)在pom.xml中&nbsp; &nbsp; <dependency>&nbsp; &nbsp; &nbsp; &nbsp; <groupId>cglib</groupId>&nbsp; &nbsp; &nbsp; &nbsp; <artifactId>cglib</artifactId>&nbsp; &nbsp; &nbsp; &nbsp; <version>3.1</version>&nbsp; &nbsp; </dependency>

哈士奇WWW

原因原因是使用多个线程。如Spring Guide中所述,请求对象在这些线程中不可用:DispatcherServlet,RequestContextListener并且RequestContextFilter都做同样的事情,即绑定HTTP请求对象添加到服务该请求的线程。这使得在请求链和会话范围内的bean可以在调用链的更下游使用。解决方案1可以使请求对象可用于其他线程,但是它对系统有一些限制,这在所有项目中可能都不可行。我从在多线程Web应用程序中访问请求范围的Bean获得了此解决方案:我设法解决了这个问题。我开始使用SimpleAsyncTaskExecutor而不是WorkManagerTaskExecutor/ ThreadPoolExecutorFactoryBean。好处是SimpleAsyncTaskExecutor永远不会重复使用线程。那只是解决方案的一半。解决方案的另一半是使用RequestContextFilter而不是RequestContextListener。RequestContextFilter(以及DispatcherServlet)具有一个threadContextInheritable属性,该属性基本上允许子线程继承父上下文。解决方案2唯一的其他选择是在请求线程内使用会话范围的Bean。就我而言,这是不可能的,因为:控制器方法用注释@Async;控制器方法将启动批处理作业,该批处理作业将线程用于并行作业步骤。
随时随地看视频慕课网APP
我要回答