Vaadin 23异步异常无法被ExceptionHandler捕获的解决方法咨询
问题描述
我有一个基础的ExceptionHandler视图,代码如下:
@ParentLayout(MainLayout.class) @AnonymousAllowed public class ExceptionHandler extends VerticalLayout implements HasErrorParameter<Exception> { static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); @Override public int setErrorParameter(BeforeEnterEvent event, ErrorParameter<Exception> parameter) { logger.error("Error", parameter.getException()); Label label = new Label(parameter.getException().getMessage()); add(label); return HttpServletResponse.SC_NOT_FOUND; } }
该视图在同步调用发生错误时可正常工作,但使用@Async特性时(示例代码如下),抛出的AsyncException无法被该ExceptionHandler捕获,仅能记录日志。请问如何正确抛出异常以使其被ExceptionHandler捕获?
异步调用代码:
ListenableFuture<DecisionMatrixPage> listenableFuture = container.queryDataAsync(); var ui = UI.getCurrent(); listenableFuture.addCallback(page -> { ui.access(() -> { //do something }); }, err -> { logger.error("Error", err); throw new AsyncException("Async error", err); });
解决方案
异步操作抛出的异常无法直接触发HasErrorParameter的处理逻辑,因为异步回调运行在独立线程中,不属于Vaadin的导航或请求处理流程。要让ExceptionHandler捕获到异步异常,需要手动将异常关联到当前UI的错误处理机制,具体可采用以下几种方式:
在UI.access中触发错误视图导航
不要在异步回调中直接抛出异常,而是在ui.access内部通过导航到ExceptionHandler视图来传递异常信息:listenableFuture.addCallback(page -> { ui.access(() -> { // 处理正常逻辑 }); }, err -> { logger.error("Error", err); ui.access(() -> { // 携带异常信息导航到错误视图 AsyncException asyncEx = new AsyncException("Async error", err); RouteParameters params = new RouteParameters("errorMsg", asyncEx.getMessage()); UI.getCurrent().navigate(ExceptionHandler.class, params); }); });扩展ExceptionHandler支持路由参数
修改ExceptionHandler,使其既能处理同步错误,也能接收异步传递的异常信息:@Route(value = "error", layout = MainLayout.class) @AnonymousAllowed public class ExceptionHandler extends VerticalLayout implements HasErrorParameter<Exception>, HasUrlParameter<String> { static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); private String asyncErrorMsg; @Override public void setParameter(BeforeEvent event, @OptionalParameter String errorMsg) { this.asyncErrorMsg = errorMsg; } @Override public int setErrorParameter(BeforeEnterEvent event, ErrorParameter<Exception> parameter) { String displayMsg = asyncErrorMsg != null ? asyncErrorMsg : parameter.getException().getMessage(); Throwable logEx = asyncErrorMsg != null ? new AsyncException(asyncErrorMsg) : parameter.getException(); logger.error("Error occurred", logEx); add(new Label(displayMsg)); return HttpServletResponse.SC_NOT_FOUND; } }利用UI全局ErrorHandler捕获异常
给当前UI设置全局ErrorHandler,捕获异步线程抛出的异常后再导航到错误视图:// 在UI初始化阶段设置全局错误处理器 UI.getCurrent().setErrorHandler(errorEvent -> { Throwable ex = errorEvent.getThrowable(); if (ex instanceof AsyncException) { UI.getCurrent().access(() -> UI.getCurrent().navigate(ExceptionHandler.class) ); } }); // 异步回调中在ui.access内抛出异常,由全局ErrorHandler捕获 listenableFuture.addCallback(page -> { ui.access(() -> { // 正常逻辑处理 }); }, err -> { logger.error("Error", err); ui.access(() -> { throw new AsyncException("Async error", err); }); });
内容的提问来源于stack exchange,提问作者alexanoid
相关产品推荐
相关产品推荐

