java - example - spring boot run async
Spring Async Uncaught Exception handler (3)
@Override
@Async
public void asyncExceptionTest() {
int i=1/0;
}
¿Cómo puedo registrar esto utilizando Spring Async framework sin tener que poner try catch en todos los métodos asíncronos? No parece pasar a la DefaultUncaughtExceptionHandler
como normal.
Actualización: Desde la primavera 4.1
Desde la primavera 4.1 es posible tener un AsyncUncaughtExceptionHandler para los métodos de @Async
void
.
Spring Reference Doc, Capítulo 34.4.5 Gestión de excepciones con @Async
... Sin embargo, con un tipo de retorno nulo, la excepción no se captura y no se puede transmitir. Para esos casos, se puede proporcionar un AsyncUncaughtExceptionHandler para manejar dichas excepciones.
Por defecto, la excepción simplemente se registra. Un AsyncUncaughtExceptionHandler personalizado se puede definir a través de AsyncConfigurer o la tarea: elemento XML controlado por anotación.
(Esta función se introdujo después de que DD presentó una solicitud de mejora: https://jira.spring.io/browse/SPR-8995 , vea los comentarios de esta respuesta)
Antes de la primavera 4.1
Parece una característica que falta en cómo manejar las excepciones de un void
devuelve el método @Async
. (No puedo encontrar ninguna pista en la referencia o en el documento de Java)
Lo que puedo imaginar de una solución: intente usar AspectJ para escribir algún tipo de envoltorio @Async
todos los métodos @Async
que registran las excepciones.
Para el término de registro, recomendaría crear una solicitud de freature en el rastreador de errores de Spring.
En primer lugar, debe crear una clase de controlador de excepciones personalizada como la siguiente;
@Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(AsyncExceptionHandler.class);
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
logger.error("Unexpected asynchronous exception at : "
+ method.getDeclaringClass().getName() + "." + method.getName(), ex);
}
}
Después de eso, debe configurar su clase de controlador de excepciones personalizada en su configuración como sigue:
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
@Autowired
private AsyncExceptionHandler asyncExceptionHandler;
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
Nota: el controlador de excepciones inyectables es una opción. Puede crear una nueva instancia para cada excepción. Mi consejo es usar Injection para la clase de controlador de excepciones, ya que el alcance predeterminado de spring es singleton, por lo que no es necesario crear una nueva instancia para cada excepción.
@Configuration
@EnableAsync
public class ExampleConfig implements AsyncConfigurer {
@Bean
public Runnable testExec() {
return new TestExec();
}
@Override
public Executor getAsyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return new HandlingExecutor(executor);
}
}
public class HandlingExecutor implements AsyncTaskExecutor {
private AsyncTaskExecutor executor;
public HandlingExecutor(AsyncTaskExecutor executor) {
this.executor = executor;
}
@Override
public void execute(Runnable task) {
executor.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
executor.execute(createWrappedRunnable(task), startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
return executor.submit(createWrappedRunnable(task));
}
@Override
public <T> Future<T> submit(final Callable<T> task) {
return executor.submit(createCallable(task));
}
private <T> Callable<T> createCallable(final Callable<T> task) {
return new Callable<T>() {
@Override
public T call() throws Exception {
try {
return task.call();
} catch (Exception e) {
handle(e);
throw e;
}
}
};
}
private Runnable createWrappedRunnable(final Runnable task) {
return new Runnable() {
@Override
public void run() {
try {
task.run();
} catch (Exception e) {
handle(e);
}
}
};
}
private void handle(Exception e) {
System.out.println("CAUGHT!");
}
}