【问题标题】:@ControllerAdvice exception handling together with @ResponseStatus@ControllerAdvice 异常处理与 @ResponseStatus
【发布时间】:2014-06-11 13:58:17
【问题描述】:

我有@ControllerAdvice 类,它处理一组异常。比我们有一些其他的例外,用@ResponseStatus 注释进行注释。为了结合这两种方法,我们使用博客文章中描述的技术:http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc,即在ControllerAdvice 中,我们按以下方式处理通用Exception:

    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        // If the exception is annotated with @ResponseStatus rethrow it and let
        // the framework handle it - like the OrderNotFoundException example
        // at the start of this post.
        // AnnotationUtils is a Spring Framework utility class.
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
            throw e;

        // Otherwise setup and send the user to a default error-view.
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }

它就像一个魅力,但是,使用这种技术会导致应用程序日志中出现以下文本的错误:

2014-06-11 15:51:32.907 错误 o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - 无法调用 @ExceptionHandler 方法:...

这是ExceptionHandlerExceptionResolver中的这段代码造成的:

try {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
            }
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
        }
        catch (Exception invocationEx) {
            logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
            return null;
        }

有没有人知道如何结合这两种方法来正确处理异常以避免日志中的错误?

谢谢, 一月

【问题讨论】:

  • 不会简单地禁用 org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver 的日志记录来解决这个问题吗?
  • 我想过,但它似乎不是问题的概念解决方案......
  • 在不重新实现类并删除日志行的情况下,您将如何解决它?当它可以像禁用日志记录一样简单时,我们为什么要提出一个复杂的解决方案。根据您的日志记录框架,您还可以创建一个根据模式过滤消息的过滤器。
  • @M. Deinum +1 是一个简单的解决方案。 log4j.logger.org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver = OFF
  • @user3730269:你能解决这个问题吗?

标签: java spring spring-mvc


【解决方案1】:

这是一个老问题,但我今天才遇到这个问题,并找到了比禁用 ExceptionHandlerExceptionResolver 的日志记录更好的解决方案。事实证明,可以通过升级到最新版本的 spring 框架来解决这个问题(4.3.8 对我有用)。 ExceptionHandlerExceptionResolver 已修复以检测原始异常是否从 @ExceptionHandler 重新抛出。在这种情况下,不再记录异常。

【讨论】:

  • 我可以要求一个经过测试的代码 sn-p,它表明它可以像描述的那样工作吗?我刚刚测试从用@ExceptionHandler 注释的方法内部抛出原始异常,它只会导致更多的错误日志记录,而不是更少的日志记录^^
【解决方案2】:

我以稍微不同的方式处理它,我认为这可以解决您的问题。

由于我知道基本上我想以不同的方式处理 404 到 500 的任何色调,我会查找 NOT_FOUND 状态并相应地发送它,这似乎有效,然后您不会重新抛出异常。

这意味着

@ControllerAdvice
public class MVCExceptionHandler {
    private static final Logger log = LogManager.getLogger();

    @ExceptionHandler(Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, HttpServletResponse res, Exception ex) throws Exception {

        // If the exception is annotated with @ResponseStatus check if it's a 404 in which case deal with it, otherwise 500 it.
        if (AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class) != null) {
            ResponseStatus rs = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
            if (HttpStatus.NOT_FOUND.equals(rs.value())) {
                res.setStatus(HttpStatus.NOT_FOUND.value());
                return new ModelAndView("error/404");
            }
        }

        log.error("Error while loading", ex);

        return new ModelAndView("error/500");

    }
}

【讨论】:

    【解决方案3】:

    我错过了import时遇到了同样的问题:

    import org.springframework.http.HttpStatus;
    

    Eclipse 没有让我在 Quick Fix 中添加它,我手动添加了该行并且它有所帮助。

    【讨论】:

      【解决方案4】:

      问题是您抛出异常而不是返回ModelAndView。本质上,您正在尝试在您的 ControllerAdvice 类中重新实现 ResponseStatusExceptionResolver。您可以这样做(例如,通过从ResponseStatusExceptionResolver 复制/粘贴代码),但除了重复之外,这种方法也是不完整的。 Spring 还为一些内置异常提供了DefaultHandlerExceptionResolver,这种方法可能无法正确处理。

      相反,我所做的并不是在我的 ControllerAdvice 类中为 Exception.class 提供包罗万象的处理程序。我不知道这是否是最好的解决方案,但这是我所知道的最好的解决方案。

      【讨论】:

      • 我突然想到你可以定义自己的HandlerExceptionResolverComposite,这样你就可以控制3个默认解析器的顺序。如果你把DefaultHandlerExceptionResolver 和ResponseStatusExceptionResolver 放在ExceptionHandlerExceptionResolver 之前(它调用你的ControllerAdvice)它应该在调用你的自定义异常处理之前处理这些默认和/或注释的异常类型。这是未经测试的,但如果它适合你,请告诉我。
      猜你喜欢
      • 2016-11-22
      • 2018-07-13
      • 1970-01-01
      • 2014-08-26
      • 2022-10-14
      • 2020-12-02
      • 2019-06-04
      • 2015-12-15
      相关资源
      最近更新 更多