Spring 异步未捕获异常处理程序

问题描述 投票:0回答:4
@Override
@Async
public void asyncExceptionTest() {
    int i=1/0;
}

如何使用 Spring Async 框架记录此内容,而不必在每个异步方法周围放置 try catch ?它似乎没有像平常一样传递到

DefaultUncaughtExceptionHandler

java spring asynchronous exception uncaughtexceptionhandler
4个回答
24
投票

@Async
方法可以配置自定义
Executor
来记录任何抛出的异常。

以下代码实现了此模式。任何标有

@Async
的方法都将使用
Executor
方法返回的
public Executor getAsyncExecutor()
。这将返回
HandlingExecutor
,它负责所有日志记录(在本例中,它只打印单词“CAUGHT!”,但您可以替换为日志记录。

@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!");
    }
}

24
投票

更新:自 Spring 4.1 起

从 Spring 4.1 开始,可以为 @Async

void
方法使用
AsyncUncaughtExceptionHandler

Spring 参考文档,第 34.4.5 章使用 @Async 进行异常管理

...然而,如果返回类型为 void,则异常不会被捕获并且无法传输。对于这些情况,可以提供 AsyncUncaughtExceptionHandler 来处理此类异常。

默认情况下,仅记录异常。可以通过 AsyncConfigurer 或任务:注释驱动的 XML 元素定义自定义 AsyncUncaughtExceptionHandler。

(此功能是在DD提出改进请求后引入的:https://jira.spring.io/browse/SPR-8995,参见本答案的评论)


Spring 4.1 之前

看起来缺少如何处理

void
返回
@Async
方法的异常的功能。 (我在参考资料或java文档中找不到任何提示)

我能想到的解决方案:尝试使用 AspectJ 编写某种包装器,围绕所有记录异常的

@Async
方法。

对于日志术语,我建议在 spring bug 跟踪器中创建一个特征请求。


20
投票

首先,您应该创建一个自定义异常处理程序类,如下所示;

@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);
        }

    }

之后,您应该在配置中设置自定义异常处理程序类,如下所示;

@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {

    @Autowired
    private AsyncExceptionHandler asyncExceptionHandler;

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return asyncExceptionHandler;
    }

}

注意:可注入异常处理程序是一个选项。您可以为每个异常创建一个新实例。我的建议是对异常处理程序类使用注入,因为 spring 的默认作用域是单例,因此不需要为每个异常创建新实例。


2
投票

您可以使用标准的 Spring AOP 方法

@Aspect
@Component
@Slf4j
public class AsyncHandler {

   @Around("@annotation(org.springframework.scheduling.annotation.Async)")
   private Object handle(ProceedingJoinPoint pjp) throws Throwable {
       try {
           Object retVal = pjp.proceed();
           return retVal;
       } catch (Throwable e) {
           log.error("in ASYNC, method: " + pjp.getSignature().toLongString() + ", args: " + AppStringUtils.transformToWellFormattedJsonString(pjp.getArgs()) + ", exception: "+ e, e);
           throw e;
       }
   }

}
© www.soinside.com 2019 - 2024. All rights reserved.