【问题标题】:Throwing an exception from @ExceptionHandler to get caught by another handler从@ExceptionHandler 抛出异常以被另一个处理程序捕获
【发布时间】:2016-03-29 10:43:03
【问题描述】:

我有一个@ControllerAdvice 类来处理来自我的 SpringMVC 控制器的异常。我想在 @ExceptionHandler 方法中捕获已知类型的异常 (RuntimeException),然后抛出 e.getCause() 异常并让该异常被同一个 @ControllerAdvice 类捕获。

示例代码:

@ControllerAdvice
public class ExceptionHandlingAdvice
{
    @ExceptionHandler( RuntimeException.class )
    private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
    {
        throw e.getCause(); // Can be of many types
    }

    // I want any Exception1 exception thrown by the above handler to be caught in this handler
    @ExceptionHandler( Exception1.class )
    private void handleAnException( final Exception1 e, final HttpServletResponse response ) throws Throwable
    {
        // handle exception
    }
}

这可能吗?

【问题讨论】:

    标签: java spring-mvc exceptionhandler


    【解决方案1】:

    您可以检查该 RuntimeException 是否为 Exception1.class 的实例并直接调用该方法:

     private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
    {
        if (e instanceof Exception1) handleAnException(e,response);
        else throw e.getCause(); // Can be of many types
    }
    

    【讨论】:

    • 如果检查不起作用,因为 RuntimeException 永远不会成为 Exception1 的子类。此外,我知道我已经可以使用 e.getCause() 显式调用handleAnException - 在 e.getCause() 可以返回许多不同类型的异常的情况下,最终会出现一大堆难看的 if-else-if 语句手动调用适当的处理程序。然后,您必须记住为该类型显式调用正确的处理程序并为每种类型应用一个新的 if 子句。如果您可以重新抛出异常并让 ControllerAdvice 也捕获它会更干净
    • 抱歉,我理解错了,并认为 Exception1 将扩展 RuntimeException 类,这就是它不会通过该处理程序的原因。不像抛出一个新的Exception1(e.getCause())那么容易;而不是抛出 e.getCause()?
    • 不幸的是,抛出一个新的异常并不会被处理程序捕获,它只是直接从处理程序中取出并在 Web 容器中导致 500 错误
    【解决方案2】:

    这晚了几年.. 但是在处理@Async 服务时遇到了这个需求——当抛出异常时,它们被包裹在ExecutionException.class 中,并希望我的控制器建议将它们引导到正确的位置处理程序,与您所处的情况相同。

    使用反射,可以收集控制器通知上的所有方法,为e.getCause().getClass()筛选匹配的@ExceptionHandler注解,然后调用第一个找到的方法。

    @ControllerAdvice
    public class ExceptionHandlingAdvice
    {
        @ExceptionHandler( RuntimeException.class )
        private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response )
        {
            if (e.getCause() != null) {
                Optional<Method> method = Arrays.stream(Rest.Advice.class.getMethods())
                        .filter(m -> {
                            // Get annotation
                            ExceptionHandler annotation = m.getAnnotation(ExceptionHandler.class);
                            // Annotation exists on method and contains cause class
                            return annotation != null && Arrays.asList(annotation.value()).contains(e.getCause().getClass());
                        })
                        .findFirst();
    
                if (method.isPresent()) {
                    try {
                        method.get().invoke(this, e.getCause(), response);
                    } catch (IllegalAccessException | InvocationTargetException ex) {
                        // Heard you like exceptions on your exceptions while excepting
                        ex.printStackTrace();
                    }
                }
            }
            
            // Handle if not sent to another
        }
    
        ... other handlers
    }
    

    没有使用void 进行测试——就我个人而言,我从我的处理程序返回ResponseEntity&lt;MyStandardErrorResponse&gt;,所以我的调用行看起来像:

    return (ResponseEntity<MyStandardErrorResponse>) method.get().invoke(this, e.getCause(), request);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-17
      • 2021-01-15
      • 2011-02-21
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多