Spring @Async and exception handling
Join the DZone community and get the full member experience.
Join For FreeIntroduction
When using Spring to asynchronously execute pieces of your code you typically use the @Async
annotation on your Spring @Component
s methods. When using the @Async
annotation Spring will use AOP (Aspect Oriented Programming) to wrap your call in a Runnable
. This Runnable
will then be scheduled for execution on a TaskExecutor
.
Exception Handling
Spring comes with a number of pre-packaged TaskExecutor
s which are documented in the Spring documentation here. In practice you will likely use the ThreadPoolTaskExecutor
or the SimpleAsyncTaskExecutor
. When doing so you might at times wonder why a task has not been executed. This happens when an exception occurs inside the method you are trying to execute asynchronous. The aforementioned task executors do not handle the exceptions so the execution fails silently. This problem can be solved by implementing your own AsyncTaskExecutor
which handles the exceptions by logging them (or in any other way you wish). Bellow you'll find an example implementation of such a AsyncTaskExecutor
.
package nl.jborsje.blog.examples; import java.util.concurrent.Callable; import java.util.concurrent.Future; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor { private AsyncTaskExecutor executor; public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) { this.executor = executor; } public void execute(Runnable task) { executor.execute(createWrappedRunnable(task)); } public void execute(Runnable task, long startTimeout) { executor.execute(createWrappedRunnable(task), startTimeout); } public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task)); } public Future submit(final Callable task) { return executor.submit(createCallable(task)); } private Callable createCallable(final Callable task) { return new Callable() { public T call() throws Exception { try { return task.call(); } catch (Exception ex) { handle(ex); throw ex; } } }; } private Runnable createWrappedRunnable(final Runnable task) { return new Runnable() { public void run() { try { task.run(); } catch (Exception ex) { handle(ex); } } }; } private void handle(Exception ex) { System.err.println("Error during @Async execution: " + ex); } }
This task executor decorates another task executor and wraps all Runnable
s and Callable
s in versions which handle the exception. In order to be able to use this task executor it has to be configured in the Spring application context as a managed bean and it has to be passed the real executor (the one that does the actual work) in its constructor. An example of such a configuration can be found bellow.
<task:annotation-driven executor="exceptionHandlingTaskExecutor" scheduler="defaultTaskScheduler" /> <bean id="exceptionHandlingTaskExecutor" class="nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor"> <constructor-arg ref="defaultTaskExecutor" /> </bean> <task:executor id="defaultTaskExecutor" pool-size="5" /> <task:scheduler id="defaultTaskScheduler" pool-size="1" />
JUnit testing
In the application context used for JUnit testing Spring beans the asynchronous execution of methods can be cumbersome. You typically want to validate the inner workings of your methods not Springs asynchronicity. To achieve this you can use the SyncTaskExecutor
provided by Spring. This task executor runs the tasks it is given immediately on the thread that adds the task instead of using a thread pool and running the tasks asynchronous. This task executor is configured in the test scope application context as follows.
<task:annotation-driven executor="synchronousTaskExecutor" scheduler="defaultTaskScheduler" /> <bean id="synchronousTaskExecutor" class="org.springframework.core.task.SyncTaskExecutor" /> <task:scheduler id="defaultTaskScheduler" pool-size="1" />
Opinions expressed by DZone contributors are their own.
Comments