使用示例
简单的演示一下使用的Demo:
@Controller @RequestMapping public class HelloController { @ResponseBody @GetMapping("/test/exception") public int testException(@RequestParam(required = false, defaultValue = "1") Integer value) { if (value == 100) { // 状态码设置为501 方便识别 throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, "服务端发生异常啦"); } return 10 / value; } }
访问以及结果的截图如下:
异常处理过程分析:
- value值传0,处理器(HandlerMethod)会抛出计算异常:ArithmeticException(/ by zero)。此异常交给默认注册上去的三个异常处理器进行处理,发现:没有一个HandlerExceptionResolver可以处理这种异常类型,所以经过DispatcherServlet#processHandlerException处理后得到的ModelAndView为null(无异常视图),因此最终的结果是:执行完afterCompletion()方法后,throw ex交给web容器去处理。这就是我们如上截图看到的结果页面,连异常代码都有,极度的不友好有木有。
若value值换位100,这样异常类型能能被处理器处理的,结果如何呢?看下截图:
异常处理过程分析:
- 此异常经过ResponseStatusExceptionResolver处理后得到一个ModelAndView,但exMv.isEmpty() = true的,因此异常不会throw出来而是return null。所以最终因为ModelAndView是空的但异常也没throw出来,最终就是把response返回喽(已经设置好返回状态码和错误消息的response),显然这种方式虽然比上面稍好(没有错误代码了),但对用户来说也是不友好的。
备注:本文并不演示最为强大、复杂的ExceptionHandlerExceptionResolver,也就是不演示@ExceptionHandler方式,因为它的详解在此处专文讲解
自定义HandlerExceptionResolver处理异常
上面两个案例都是使用Spring MVC内置的异常处理器,显然用户体验均非常不友好。所以在实际生产环境,必须是需要自己来处理异常(页面)的,下面采用自定义HandlerExceptionResolver方式给出Demo案例,仅供参考:
1、自定义异常处理类并配置上去(本例以匿名内部类方式处理):
@Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { // 自定义异常处理器一般请放在首位 exceptionResolvers.add(0, new AbstractHandlerExceptionResolver() { @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 若是自定义的业务异常,那就返回到单页面异常页面 if (ex instanceof BusinessException) { return new ModelAndView("/business.jsp"); } else { // 否则统一到统一的错误页面 return new ModelAndView("/error.jsp"); } } }); } }
2、对应的准备两个错误页面
3、请求示例截图如下(value=0 or 100):
相关阅读
@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】
总结
我书写本文的目的主要是让大家"善待异常"和重视异常的处理,我认为一个好的程序员应该对异常的处理都是相对妥当的,而不是坐而不理。我相信你在工作中一定遇到过异常处理不妥当而到处吐槽你的团队的现象,那么看了本文后,动起来吧,重视起来吧,处理起来吧,而不是只剩下抱怨了…
不客气的说一句话:微服务的难点在于它的治理,若你统一了技术栈,规范了各种情况的处理(比如远程调用、异常处理、状态码返回、熔断处理等),它将会让你的开发效率、监控、报警问题、定位成本大大减少。所以从某种程度上,它是服务开发、服务治理非常非常重要的一环,所以"代码架构师"真的是非常重要的,而不是什么都只做事后补救,事前预防或许来得更有效。
当然,本文介绍的方案还是不够的,因为更为重要的@ExceptionHandler方式我放在了下一篇文章单独讲解。在当下Rest流行的生产环境下,使用更加强大、更方便的@ExceptionHandler方式是首选。对于它的解释说明和使用Demo,出门右拐可以给你惊喜。同时此时你是否疑问过这个问题:若异常处理器里面抛出了异常,将会怎样呢?答案也一样的出门右拐吧~