为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

MM们 2019-11-11 14:02:35
我有一个控制器,希望每个会话都唯一。根据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。
查看完整描述

3 回答

?
qq_遁去的一_1

TA贡献1725条经验 获得超7个赞

问题不在于您的Spring注释,而在于您的设计模式。您将不同的作用域和线程混合在一起:

  • 单身人士

  • 会话(或请求)

  • 作业线程池

单身人士可以在任何地方使用,没关系。但是,会话/请求范围在附加到请求的线程之外不可用。

即使请求或会话不再存在,异步作业也可以运行,因此无法使用依赖于请求/会话的bean。同样也没有办法知道,如果您正在另一个线程中运行作业,哪个线程是发起者请求(这意味着aop:proxy在这种情况下没有帮助)。


我觉得你的代码看起来像要作合同 ReportController,报表制作,UselessTask和ReportPage之间。有没有办法只使用一个简单的类(POJO)来存储UselessTask中的数据并在ReportController或ReportPage中读取它,而不再使用ReportBuilder


查看完整回答
反对 回复 2019-11-11
?
largeQ

TA贡献2039条经验 获得超7个赞

如果还有其他人坚持同一观点,以下解决了我的问题。


在web.xml中


 <listener>

            <listener-class>

                    org.springframework.web.context.request.RequestContextListener 

            </listener-class>

  </listener>

会话中组件


@Component

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

在pom.xml中


    <dependency>

        <groupId>cglib</groupId>

        <artifactId>cglib</artifactId>

        <version>3.1</version>

    </dependency>


查看完整回答
反对 回复 2019-11-11
?
哈士奇WWW

TA贡献1799条经验 获得超6个赞


原因


原因是使用多个线程。如Spring Guide中所述,请求对象在这些线程中不可用:


DispatcherServlet,RequestContextListener并且RequestContextFilter都做同样的事情,即绑定HTTP请求对象添加到服务该请求的线程。这使得在请求链和会话范围内的bean可以在调用链的更下游使用。


解决方案1


可以使请求对象可用于其他线程,但是它对系统有一些限制,这在所有项目中可能都不可行。我从在多线程Web应用程序中访问请求范围的Bean获得了此解决方案:


我设法解决了这个问题。我开始使用SimpleAsyncTaskExecutor而不是WorkManagerTaskExecutor/ ThreadPoolExecutorFactoryBean。好处是SimpleAsyncTaskExecutor永远不会重复使用线程。那只是解决方案的一半。解决方案的另一半是使用RequestContextFilter而不是RequestContextListener。RequestContextFilter(以及DispatcherServlet)具有一个threadContextInheritable属性,该属性基本上允许子线程继承父上下文。


解决方案2


唯一的其他选择是在请求线程内使用会话范围的Bean。就我而言,这是不可能的,因为:


控制器方法用注释@Async;

控制器方法将启动批处理作业,该批处理作业将线程用于并行作业步骤。


查看完整回答
反对 回复 2019-11-11
  • 3 回答
  • 0 关注
  • 854 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信