Spring Async, Как включить область запроса в исполнителе асинхронных задач

Я пытаюсь использовать @Async de spring, а в моем сервисе я использую bean-компонент с областью действия session, всегда получаю следующую ошибку:

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!

Как уже упоминалось, сеанс не найден, а запрос уже завершен. Я реализовал AsyncConfigurer, чтобы переопределить ThreadPoolTaskExecutor: ниже мой код

Контроллер:

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

// Заменить 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;
    }
}

Я подписался на этот отклик

Не уверен, что это поможет, но вместо автоматического подключения classSession попробуйте передать classSession в качестве параметра в do().

Andrew S 01.10.2018 16:58

@AndrewS Спасибо за ваше предложение, но нет, это невозможно ...

BERGUIGA Mohamed Amine 01.10.2018 17:21
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
2
2 536
2

Ответы 2

Вместо этого вам нужно переопределить метод выполнения:

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

И обновите свой ContextAwareCallable до:

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

Я не гуру, но это решение мне помогло.

Как только вы аннотируете метод с помощью @Async, он запускает метод execute() для класса ThreadPoolTaskExecutor, а не для submit() или submitListenable().

По моему решению он возвращает CompletableFuture. Странно, потому что execute ничего не возвращает.

Надеюсь, это кому-то поможет.

У меня есть приложение multiTenant с асинхронными методами. Кто-то возвращает CompletableFuture. Как уже было сказано, теперь у меня работает!

@Configuration

@EnableAsync открытый класс 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;
}

}

открытый класс ContextAwarePoolExecutor расширяет 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();
}

}

Другие вопросы по теме