Java / Spring MVC:向子线程提供请求上下文

时间:2018-09-25 16:20:25

标签: java spring multithreading spring-mvc

我有一个问题,我想将Spring WebMVC应用程序的某些进程外包到单独的线程中。这很容易并且有效,直到我要使用使用全局请求的类userRightService为止。这在线程中不可用,我们遇到了一个问题,这几乎可以理解。

这是我的错误:

java.lang.RuntimeException:
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'scopedTarget.userRightsService': Scope 'request' 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: Cannot ask for request attribute - 
request is not active anymore!

好的,足够清楚。我正在尝试通过实施以下解决方案来保留请求上下文:

How to enable request scope in async task executor

这是我的可运行类:

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class myThread implements Runnable {

  private RequestAttributes context;

  public DataExportThread(RequestAttributes context) {
    this.context = context;
  }

  public void run() {
    RequestContextHolder.setRequestAttributes(context);

产生它的地方:

final DataExportThread dataExportThread = 
   new myThread(RequestContextHolder.currentRequestAttributes());

final Thread thread = new Thread(myThread);
thread.setUncaughtExceptionHandler((t, e) -> {...});
thread.start();

据我了解,我们将currentRequestAttributes存储在线程中,然后在运行时将它们还原为currentRequestAttributes ...对我来说听起来很可靠,但错误仍然存​​在。我认为为我的案例调整解决方案时犯了一些错误。也许有人可以帮助我找到错误。

在我经历大量具有不同解决方案的stackoverflow线程之前(请参见下文),因此我可以尝试其他方法,但是对于我来说,这似乎是最清晰,最简单的方法,所以我希望有人可以帮助我找到错误在实施中进行解释,或者解释为什么这是错误的方法。

我已经尝试过此方法,但没有成功:

这很重要:

<org.springframework-version>4.3.4.RELEASE</org.springframework-version>

顺便说一句:我知道以某种方式重组应用程序会更好,线程中不需要该请求,但是在那种情况下这很复杂,我真的希望我能避免这种情况。

-

Edit1:

无法在线程中创建的Bean是这样启动的:

@Service("userRightsService")
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserRightsService {

-

Edit2:

我也尝试过这个:

但是上下文始终是空的...

2 个答案:

答案 0 :(得分:1)

我不确定您是如何创建/注入UserRightsService的,所以无法重现该问题,但是我有一些建议可以尝试。

我想问题是RequestAttributes在请求结束时失效(这就是为什么异常Cannot ask for request attribute - request is not active anymore),这是在任务运行时发生的。

相反,您可以尝试在产生线程的位置注入UserRightsService,然后将此实例作为参数传递给线程。这样,UserRightsService应该可以毫无问题地创建,因为请求应该仍然可用。

即使如此,在请求结束后尝试访问RequestAttributes可能也会失败。在那种情况下,我建议在请求结束之前(即在运行线程之前)复制所需的所有值。

如果这对您不起作用,请提供更多有关如何初始化任务中的UserRightsService的信息。

祝你好运!

P.S .:我认为线程类中的作用域注释是无用的,因为任务对象是手动创建的,而不是由spring管理的。

答案 1 :(得分:0)

对于那些正在搜索的人。借助Master_Ex的提示,我找到了解决方案:

在可运行环境中:

private HttpServletRequest request;

public void run() {

    final RequestContextListener rcl = new RequestContextListener();
    final ServletContext sc = request.getServletContext();
    rcl.requestInitialized(new ServletRequestEvent(sc, request));

然后在UserRightService中,我调用一个执行以下操作的函数:

    SecurityContext context = SecurityContextHolder.getContext();
    Authentication auth = context.getAuthentication();

    context.setAuthentication(getDataExportAuthentication(exportingUser));

@Master_Ex的谢谢,您的帖子非常有帮助。很抱歉,我来不及给您赏金,否则我会把它标记为正确的赏金。