@Async that throws a runtime exception. What is the default behavior if no custom AsyncUncaughtExceptionHandler is configured?import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncService { @Async public void asyncMethod() { throw new RuntimeException("Error in async method"); } }
By default, Spring uses SimpleAsyncUncaughtExceptionHandler which logs exceptions thrown from @Async methods that return void. The exception does not propagate to the caller thread.
CompletableFuture<String>. Which method signature is correct to enable catching exceptions in the caller thread?Returning a CompletableFuture allows exceptions to be captured and handled asynchronously by the caller. Throwing exceptions directly in the method body without wrapping them in a Future will not propagate exceptions properly.
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Component public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { // Missing logging implementation } }
The handler method is empty and does not log or handle the exception, so exceptions are effectively ignored.
Implementing AsyncConfigurer and overriding getAsyncUncaughtExceptionHandler() is the standard way to globally set a custom handler for async exceptions.
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.CompletableFuture; @Service public class AsyncService { @Async public CompletableFuture<String> asyncMethod() { return CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Async error"); }); } } // Caller code AsyncService service = new AsyncService(); service.asyncMethod() .exceptionally(ex -> { System.out.println("Caught: " + ex.getMessage()); return "Recovered"; }) .thenAccept(result -> System.out.println("Result: " + result));
The exceptionally block catches the RuntimeException. ex.getMessage() returns "Async error". So it prints "Caught: Async error" and then "Result: Recovered".