BadPractice8 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki
To handle exceptions that emerged inside a thread, developers could have have some problems that described in the correctness error. For example, some of them may try to use try-catch block to catch exceptions that made by void return type. At the same time, some may wnat to use @ExceptionHandler
to deal with it. Obviously, those are correctness problems.
Therefore, I would like to introduce what should we do handle exceptions that cause by asynchonous method in Spring.
With @EnableAsync
annotation, Spring will enable its asynchonous method execution capability. Without any configuration, Spring will search for a org.springframework.core.task.TaskExecutor
bean or an java.util.concurrent.Executor
bean as the thread pool definition.(@EnableAsync
Doc)
-
void
return type
However, in this default case, exceptions thrown by void
return type are uncaught exceptions. ErrorHandler
in base class AbstractMessageListenerContainer
will be invoked while there is an uncaught exception, but the ErrorHanler
will only perform error-level logging.
/**
* Set the ErrorHandler to be invoked in case of any uncaught exceptions thrown
* while processing a Message.
* <p>By default, there will be <b>no</b> ErrorHandler so that error-level
* logging is the only result.
*/
public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
So, these uncaught exceptions will not be propagated to the caller and can not be handled correctly.
In this scenario, Spring provides developers some ways to customize the their own AsyncUncaughtExceptionHandler
, including to implement interface AsyncConfigurer
, to extends class AsyncConfigurerSupport
which implements AsyncConfigurer
.
There are two solutions for asynchonous method exception handling, each of those is for certain return type.
-
void
return type
For those have void return type, we have two choices. We can implement AsyncConfigurer
interface or extends AsyncConfigurerSupport
class to customize AsyncUncaughtExceptionHandler
.
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
ErrorLogger.getInstance().log(String.format("Invoking async method: '%s'", method), ex);
}
};
}
}
Or we can simply extends AsyncConfigurerSupport
for only AsyncUncaughtExceptionHandler
.
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
ErrorLogger.getInstance().log(String.format("Invoking async method: '%s'", method), ex);
}
};
}
}