How to enable request scope in async task executor

匿名 (未验证) 提交于 2019-12-03 02:13:02

问题:

In my app I have some async web services. Server accept request, return OK response and start processing request with AsyncTaskExecutor. My question is how to enable request scope here because in this processing I need to get class which is annotated by:

@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) 

Now I get exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.requestContextImpl': 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: 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. 

because it runs in SimpleAsyncTaskExecutor and not in DispatcherServlet

my async processing of request

taskExecutor.execute(new Runnable() {      @Override     public void run() {         asyncRequest(request);     } }); 

where taskExecutor is:

<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" /> 

回答1:

We ran into the same problem - needed to execute code in the background using @Async, so it was unable to use any Session- or RequestScope beans. We solved it the following way:

  • Create a custom TaskPoolExecutor that stores scoped information with the tasks
  • Create a special Callable (or Runnable) that uses the information to set and clear the context for the background thread
  • Create an override configuration to use the custom executor

Note: this will only work for Session and Request scoped beans, and not for security context (as in Spring Security). You'd have to use another method to set the security context if that is what you're after.

Note2: For brevity, only shown the Callable and submit() implementation. You can do the same for the Runnable and execute().

Here is the code:

Executor:

public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {     @Override     public <T> Future<T> submit(Callable<T> task) {         return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));     }      @Override     public <T> ListenableFuture<T> submitListenable(Callable<T> task) {         return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));     } } 

Callable:

public class ContextAwareCallable<T> implements Callable<T> {     private Callable<T> task;     private RequestAttributes context;      public ContextAwareCallable(Callable<T> task, RequestAttributes context) {         this.task = task;         this.context = context;     }      @Override     public T call() throws Exception {         if (context != null) {             RequestContextHolder.setRequestAttributes(context);         }          try {             return task.call();         } finally {             RequestContextHolder.resetRequestAttributes();         }     } } 

Configuration:

@Configuration public class ExecutorConfig extends AsyncConfigurerSupport {     @Override     @Bean     public Executor getAsyncExecutor() {         return new ContextAwarePoolExecutor();     } } 


回答2:

There is no way to get a request scoped object in an child async thread, since the original parent request processing thread may have already committed the response to the client and all the request objects are destroyed. One way to handle such scenarios is to use custom scope, like SimpleThreadScope.

one problem with SimpleThreadScope is that the child threads will not inherit parents scope variables, because it uses simple ThreadLocal internally. To overcome that implement a custom scope which is exactly similar to SimpleThreadScope but uses InheritableThreadLocal internally. For more info reg this Spring MVC: How to use a request-scoped bean inside a spawned thread?



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!