Mixing Java `ExecutorService` with Spring `TaskExecutor`

Viewed 1013

I have a piece of Java code that I previously used without Spring that looks like this:

// `Callable` instead of `Runnable` because we need to throw exceptions
public MyTask extends Callable<Void> {
  @Override
  public Void call() throws Exception { ... }
}

public class MyTasksRunner {

  private final ExecutorService executorService;

  ...

  public void run() throws Exception {
    List<MyTask> tasks = ...;
    var futures = executorService.invokeAll(tasks);
    for (var future : futures) {
      // Rethrow any exceptions happened in the threads.
      future.get();
    }
  }
}

Now I'm merging this code into a larger Spring Boot application that has async enabled. It configures a TaskExecutor, which doesn't have the same interface as ExecutorService. A TaskExecutor can only run Runnables, not Callables.

I can probably have a TaskExecutor bean for async Spring, and another ExecutorService bean for the MyTasksRunner code at the same time. But I wonder what options I have if I want to merge those:

  • Can I tell Spring to use an ExecutorService for its async stuff?
  • Can I convert my Callable code to use Runnables instead, while still being able to propagate exceptions from the tasks?

I also thought about just making MyTask a Spring component and annotating it with @Async, but I don't really like that it makes the MyTask* code tied to Spring.

1 Answers

Yes, you can convert your Callable task to Runnable as I see you don't expect any return value. But with one condition - you cant throw Checked Exception however you may continue throwing Runtime Exception.

Also, yes you can define Executor bean as below to inject ExecutorService

@Bean
public Executor taskExecutor() {

    ExecutorService executor = Executors.newFixedThreadPool(2);

    return executor;
}

If you dont define an Executor bean, Spring creates SimpleAsyncTaskExecutor and uses that.

Related