关于java:Spring-MVC-十异常处理

44次阅读

共计 4031 个字符,预计需要花费 11 分钟才能阅读完成。

异样是每一个利用必须要解决的问题。

Spring MVC 我的项目,如果不做任何的异样解决的话,产生异样后,异样堆栈信息会间接抛出到页面。

比方,咱们在 Controller 写一个异样:

    @GetMapping(value="/hello",produces={"text/html; charset=UTF-8"})
    @ResponseBody
    public String hello(ModelAndView model){
        int c = 100 / 0;
        return "<h1>User info</h1>";
    }

浏览器拜访:

用户体验相当不好,所以个别状况下,利用必须要想方法解决异样。

异样解决形式

常见的异样解决形式,无非:

  1. 利用中对所有可能产生异样的中央,都 try catch,捕捉异样后做相应的解决。
  2. 集中处理异样。

第一种形式显然不好,一方面是代码中须要到处都写 try catch,万一某一段代码因为程序员的忽略没有写,异样就会抛出到前台,很不好。

另外,某些状况下异样是不能捕捉的,比方须要事务处理的代码,捕捉异样后会影响到事务回滚。

所以,咱们还是须要想方法用第 2 中形式来解决异样。n 多年前已经做过一个摩托罗拉的我的项目,其中一项需要就是对一个线上零碎的异样做解决、不容许异样信息抛出到前台页面。当初那个我的项目并没有采纳相似 SpringMVC 的框架,所以最终提出了用 filter、在 filter 中拦挡异样的解决计划并且被客户驳回。

其实 SpringMVC 的对立异样解决计划和我下面我的项目中采纳的计划十分相似。

SpringMVC 的异样解决

Spring MVC 提供了如下异样解决选项:

  1. @ExceptionHandle
  2. @ExceptionHandle + @ControllerAdvice
  3. 自定义异样处理器 HandlerExceptionResolver

@ExceptionHandle

@ExceptionHandle 能够作用在 Controller 中,比方,在下面产生异样的 Controller 中减少一个 @ExceptionHandle 注解的办法:

    @GetMapping(value="/hello",produces={"text/html; charset=UTF-8"})
    @ResponseBody
    public String hello(ModelAndView model){
        int c = 100 / 0;
        return "<h1>User info</h1>";
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public String handle(Exception ex){return "错了在 helloworld Controller error msg is ===";}

运行:

阐明 Hello 办法中产生的异样,曾经被 handle 办法解决,前台页面不再会出现异常信息。

问题解决了!

然而 @ExceptionHandle 只在以后 Controller 文件中失效,也就是说,以后 Controller 中的办法、或者办法调用的 service 层、dao 层等产生的异样,才会被捕捉到。其余 Controller 中产生的异样仍然不会被捕捉。

这样的话,就须要对每一个 Controller 减少 @ExceptionHandle 进行解决,解决起来还是有点麻烦。

对立异样解决

@ExceptionHandle + @ControllerAdvice 能够实现对立异样解决。

@ControllerAdvice 注解能够实现:

On startup, RequestMappingHandlerMapping and ExceptionHandlerExceptionResolver detect controller advice beans and apply them at runtime. Global @ExceptionHandler methods, from an @ControllerAdvice, are applied after local ones, from the @Controller. By contrast, global @ModelAttribute and @InitBinder methods are applied before local ones.

SpringMVC 启动的过程中,RequestMappingHandlerMapping 和 ExceptionHandlerExceptionResolver 会检测到 advice bean 并且在运行时会应用他们。在 @ControllerAdvice 中的 @ExceptionHandler 会变成一个全局的异样处理器、在本地异样处理器之后失效。并且,@ModelAttribute 和 @InitBinder 会在本地的之后失效。

这段话的意思就是,@ExceptionHandle + @ControllerAdvice 之后,@ExceptionHandle 就会变成全局异样处理器。所谓的本地异样处理器,就是写在 Controller 中的 @ExceptionHandle 异样处理器。

这个工作是 ExceptionHandlerExceptionResolver 干的,源码中能够看到:

@Nullable
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {

        Class<?> handlerType = null;
        // 先找 "local" 异样处理器
        if (handlerMethod != null) {
            // Local exception handler methods on the controller class itself.
            // To be invoked through the proxy, even in case of an interface-based proxy.
            handlerType = handlerMethod.getBeanType();
            ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
            if (resolver == null) {resolver = new ExceptionHandlerMethodResolver(handlerType);
                this.exceptionHandlerCache.put(handlerType, resolver);
            }
            Method method = resolver.resolveMethod(exception);
            if (method != null) {return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
            }
            // For advice applicability check below (involving base packages, assignable types
            // and annotation presence), use target class instead of interface-based proxy.
            if (Proxy.isProxyClass(handlerType)) {handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
            }
        }
        // 再找 advice 的异样处理器
        for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {ControllerAdviceBean advice = entry.getKey();
            if (advice.isApplicableToBeanType(handlerType)) {ExceptionHandlerMethodResolver resolver = entry.getValue();
                Method method = resolver.resolveMethod(exception);
                if (method != null) {return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
                }
            }
        }

        return null;
    }

验证一下。

加一个 MyGlobalExceptionController:

@ControllerAdvice
public class MyGlobalExceptionController {@ExceptionHandler(Exception.class)
    @ResponseBody
    public String handle(Exception ex){return return "错了 MyGlobalExceptionController error msg is ===";}
}

先不去掉 HelloWorldController 中的异样处理器,运行 hello:

失效的是 HelloWorldController 中的异样处理器。

而后去掉 HelloWorldController 中的异样处理器:

写在 MyGlobalExceptionController 中的全局异样处理器失效!

自定义异样处理器 HandlerExceptionResolver

实现接口 HandlerExceptionResolver,或者扩大虚构类 AbstractHandlerMethodExceptionResolver(勉强能够思考),定义本人的异样处理器。

其实,非必要(没想到有什么必要性)不倡议!

上一篇 Spring MVC 九:Context 层级(基于配置)

正文完
 0