spring 当前线程的范围“会话”未激活;IllegalStateException:找不到线程绑定请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21286675/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 06:29:10  来源:igfitidea点击:

Scope 'session' is not active for the current thread; IllegalStateException: No thread-bound request found

springwicket

提问by Jon

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:

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

1. Initial web configuration

1. 初始网页配置

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.

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

I've added the following to my web.xmlas shown in the documentation:

我已将以下内容添加到我web.xml的文档中:

<listener>
  <listener-class>
    org.springframework.web.context.request.RequestContextListener
  </listener-class>
</listener>

2. Scoped beans as dependencies

2. 作用域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.

如果您想将(例如)一个 HTTP 请求作用域 bean 注入另一个 bean,您必须注入一个 AOP 代理来代替作用域 bean。

I've annotated the bean with @Scopeproviding the proxyModeas shown below:

我已经注释了 bean 并@Scope提供proxyMode如下所示的内容:

@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.

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

Update 1

更新 1

Below is my component scan. I have the following in web.xml:

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

以及以下内容AppConfig.java

@Configuration
@EnableAsync
@EnableCaching
@ComponentScan("org.example")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements AsyncConfigurer {
  ...
  ...
}

Update 2

更新 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.gzto megafileupload.

我创建了一个可重现的测试用例。这是一个小得多的项目,因此存在差异,但会发生相同的错误。有很多文件,所以我把它作为一个上传tar.gzmegafileupload

采纳答案by Jon

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,因为他指出了原因。

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:

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

DispatcherServlet, RequestContextListenerand RequestContextFilterall 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.

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

Solution 1

解决方案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:

可以使请求对象对其他线程可用,但它对系统施加了一些限制,这可能不适用于所有项目。我从Accessing request scoped beans in a multi-threaded web application得到了这个解决方案:

I managed to get around this issue. I started using SimpleAsyncTaskExecutorinstead of WorkManagerTaskExecutor/ ThreadPoolExecutorFactoryBean. The benefit is that SimpleAsyncTaskExecutorwill never re-use threads. That's only half the solution. The other half of the solution is to use a RequestContextFilterinstead of RequestContextListener. RequestContextFilter(as well as DispatcherServlet) has a threadContextInheritableproperty which basically allows child threads to inherit the parent context.

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

Solution 2

解决方案2

The only other option is to use the session scoped bean inside the request thread. In my case this wasn't possible because:

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

  1. The controller method is annotated with @Async;
  2. The controller method starts a batch job which uses threads for parallel job steps.
  1. 控制器方法用@Async;注释。
  2. 控制器方法启动一个批处理作业,该作业使用线程进行并行作业步骤。

回答by Martin Strejc

The problem is not in your Spring annotations but your design pattern. You mix together different scopes and threads:

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

  • singleton
  • session (or request)
  • thread pool of jobs
  • 单身人士
  • 会话(或请求)
  • 作业线程池

The singleton is available anywhere, it is ok. However session/request scope is not available outside a thread that is attached to a request.

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

Asynchronous job can run even the request or session doesn't exist anymore, so it is not possible to use a request/session dependent bean. Also there is no way to know, if your are running a job in a separate thread, which thread is the originator request (it means aop:proxy is not helpful in this case).

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



I think your code looks like that you want to make a contractbetween ReportController, ReportBuilder, UselessTask and ReportPage. Is there a way to use just a simple class (POJO) to store data from UselessTask and read it in ReportController or ReportPage and do not use ReportBuilderanymore?

我认为你的代码看起来像你想要在 ReportController、ReportBuilder、UselessTask 和 ReportPage 之间建立契约。有没有办法只使用一个简单的类(POJO)来存储来自 UselessTask 的数据并在 ReportController 或 ReportPage 中读取它并且不再使用 ReportBuilder

回答by Mohit

If anyone else stuck on same point, following solved my problem.

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

In web.xml

在 web.xml 中

 <listener>
            <listener-class>
                    org.springframework.web.context.request.RequestContextListener 
            </listener-class>
  </listener>

In Session component

在会话组件中

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

In pom.xml

在 pom.xml 中

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>

回答by Mani

As per documentation:

由于每个文件

If you are accessing scoped beans within Spring Web MVC, i.e. within a request that is processed by the Spring DispatcherServlet, or DispatcherPortlet, then no special setup is necessary: DispatcherServlet and DispatcherPortlet already expose all relevant state.

如果您在 Spring Web MVC 中访问作用域 bean,即在由 Spring DispatcherServlet 或 DispatcherPortlet 处理的请求中,则不需要特殊设置:DispatcherServlet 和 DispatcherPortlet 已经公开了所有相关状态。

If you are runnning outside of Spring MVC ( Not processed by DispatchServlet) you have to use the RequestContextListenerNot just ContextLoaderListener.

如果您在 Spring MVC 之外运行(不由 DispatchServlet 处理),则必须使用RequestContextListenerNot just ContextLoaderListener.

Add the following in your web.xml

在您的 web.xml 中添加以下内容

   <listener>
            <listener-class>
                    org.springframework.web.context.request.RequestContextListener 
            </listener-class>
    </listener>        

That will provide session to Spring in order to maintain the beans in that scope

这将为 Spring 提供会话以维护该范围内的 bean

Update : As per other answers , the @Controlleronly 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) .

更新:根据其他答案,@Controller当您在 Spring MVC 上下文中时唯一明智的,因此 @Controller 在您的代码中没有发挥实际作用。您仍然可以将 bean 注入具有会话范围/请求范围的任何位置(您不需要 Spring MVC/Controller 仅在特定范围内注入 bean)。

Update : RequestContextListenerexposes 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.

更新: RequestContextListener仅向当前线程公开请求。
您在两个地方自动装配了 ReportBuilder

1. ReportPage- 您可以在这里看到 Spring 正确地注入了 Report Builder,因为我们仍然在相同的 web 线程中。我确实更改了代码的顺序,以确保 ReportBuilder 像这样注入到 ReportPage 中。

log.info("ReportBuilder name: {}", reportBuilder.getName());
reportController.getReportData();

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 by RequestContextListener.


2. UselessTasklet- 我们在这里遇到了异常,因为这是由 Spring Batch 创建的不同线程,其中 Request 没有由RequestContextListener.


You should have different logic to create and inject ReportBuilderinstance to Spring Batch ( May Spring Batch Parameters and using Future<ReportBuilder>you can return for future reference)


您应该有不同的逻辑来创建和注入ReportBuilder实例到 Spring Batch(Future<ReportBuilder>可以使用 Spring Batch 参数并使用您可以返回以供将来参考)

回答by Noman Akhtar

I fixed this issue by adding following code in my file.

我通过在我的文件中添加以下代码解决了这个问题。

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

XML configuration -

XML 配置 -

<listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener 
        </listener-class>
</listener>

Above we can do using Java configuration -

上面我们可以使用 Java 配置 -

@Configuration
@WebListener
public class MyRequestContextListener extends RequestContextListener {
}

How to add a RequestContextListener with no-xml configuration?

如何添加无 xml 配置的 RequestContextListener?

I am using spring version 5.1.4.RELEASE and no needto add below changes in pom.

我正在使用 spring 版本 5.1.4.RELEASE 并且不需要在pom.xml中添加以下更改。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.10</version>
</dependency>

回答by Deepak

https://stackoverflow.com/a/30640097/2569475

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

在实际 Web 请求之外使用请求范围的 bean

回答by Deepak

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.

我的回答是指 OP 所描述的一般问题的一个特例,但我会添加它,以防万一它对某人有帮助。

When using @EnableOAuth2Sso, Spring puts an OAuth2RestTemplateon the application context, and this component happens to assume thread-bound servlet-related stuff.

使用时@EnableOAuth2Sso,Spring 将 anOAuth2RestTemplate放在应用程序上下文中,而该组件恰好承担了与线程绑定的 servlet 相关的内容。

My code has a scheduled async method that uses an autowired RestTemplate. This isn't running inside DispatcherServlet, but Spring was injecting the OAuth2RestTemplate, which produced the error the OP describes.

我的代码有一个预定的异步方法,它使用自动装配的RestTemplate. 这不是在内部运行DispatcherServlet,而是 Spring 正在注入OAuth2RestTemplate,这产生了 OP 描述的错误。

The solution was to do name-based injection. In the Java config:

解决方案是进行基于名称的注入。在 Java 配置中:

@Bean
public RestTemplate pingRestTemplate() {
    return new RestTemplate();
}

and in the class that uses it:

并在使用它的类中:

@Autowired
@Qualifier("pingRestTemplate")
private RestTemplate restTemplate;

Now Spring injects the intended, servlet-free RestTemplate.

现在 Spring 注入了预期的、无 servlet 的RestTemplate.

回答by Kunwar Babu

You just need to define in your bean where you need a different scope than default singleton scope except prototype. For example:

您只需要在 bean 中定义需要与默认单例范围不同的范围(原型除外)。例如:

<bean id="shoppingCart" 
   class="com.xxxxx.xxxx.ShoppingCartBean" scope="session">
   <aop:scoped-proxy/> 
</bean>