简体   繁体   中英

Spring Async, How to enable request scope in async task executor

I try to use @Async de spring, And in my service I use a bean with scope session , always I got the bellow error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.classSession': 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 session found and request already completed - cannot create new session!

As is mentioned, No session found and request already completed. I implemented AsyncConfigurer in order to overide the ThreadPoolTaskExecutor: bellow my code

Controller:

@Autowired 
MyService myService;

@RequestMapping(value = "/doIt", method = RequestMethod.PUT)
public HttpEntity initiateCurrent(..){

myService.do();
...
}

MyService

@Autowired 
ClassWithScopeSession classSession;

@Async("taskExecutor")
public void do(){
....
...
classSession.doService();
}

//Overide the ThreadPoolTaskExecutor

public class ContextAwareCallable<T> implements Callable<T> {
    private Callable<T> task;
    private RequestAttributes context;

    @Autowired
    private ApplicationContext appContext;

    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();
        }
    }
}

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()));
    }
}

@Configuration
@EnableAsync
public class ExecutorConfig  implements AsyncConfigurer {

    @Override
    @Bean(name="taskExecutor")
    public Executor getAsyncExecutor() {
        return new ContextAwarePoolExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

I followed this response

You need to override execute method instead:

@Override
public void execute(Runnable task) {
    super.execute(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}

And update your ContextAwareCallable to:

public class ContextAwareCallable implements Runnable {

    private Runnable task;
    private RequestAttributes context;

    public ContextAwareCallable(Runnable task, RequestAttributes context) {
        this.task = task;
        this.context = context;
    }

    @Override
    public void run() {
        if (context != null) {
            RequestContextHolder.setRequestAttributes(context);
        }

        task.run();
    }
}

I am not a guru, but this solution worked for me.

Once you annotate a method with @Async , it runs execute() method on ThreadPoolTaskExecutor class, not submit() or submitListenable() .

On my solution it returns CompletableFuture . It's strange, because execute does not return anything.

Hope it will help someone.

I have a multiTenant app with async methods. Someone returns a CompletableFuture. As said before, now it works for me!

@Configuration

@EnableAsync public class DashBoardAsyncExecutorConfig {

@Bean("MyTaskExecutor")
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ContextAwarePoolExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(5);
    executor.setQueueCapacity(500);
    executor.setAllowCoreThreadTimeOut(true);
    executor.setThreadNamePrefix("thread");
    executor.initialize();
    return 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()));

   }
   
   //FOR COMPLETABLE FUTURE
   @Override
   public void execute(Runnable task) {
       super.execute(new ContextAwareCallableRunnable(task, RequestContextHolder.currentRequestAttributes()));
   }       

}

public class ContextAwareCallableRunnable<T> implements Runnable {
private Runnable task;
private RequestAttributes context;

public ContextAwareCallableRunnable(Runnable task, RequestAttributes context) {
    this.task = task;
    this.context = context;
}

@Override
public void run() {
    if (context != null) {
        RequestContextHolder.setRequestAttributes(context);
    }
    task.run();
}

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM