SpringBoot(10) -- Web开发-- 异常处理

SpringBoot2学习笔记

源码地址

四、Web开发

4.9)异常处理

4.9.1)异常处理默认规则

默认情况下,Spring Boot提供/error处理所有错误的映射;

对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据,访问不存在路径内容页面如下:

 

要对其进行自定义,error/下的4xx,5xx页面会被自动解析,添加view页面4xx.html,代码如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
  <meta name="description" content="">
  <meta name="author" content="ThemeBucket">
  <link rel="shortcut icon" href="#" type="image/png">
​
  <title>404 Page</title>
​
  <link href="css/style.css" rel="stylesheet">
  <link href="css/style-responsive.css" rel="stylesheet">
​
  <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
  <!--[if lt IE 9]>
  <script src="js/html5shiv.js"></script>
  <script src="js/respond.min.js"></script>
  <![endif]-->
</head>
​
<body class="error-page">
​
<section>
    <div class="container ">
​
        <section class="error-wrapper text-center">
            <h1><img alt="" src="images/404-error.png"></h1>
            <h2 th:text="${status}">page not found</h2>
            <h3 th:text="${message}">We Couldn’t Find This Page</h3>
            <a class="back-btn" th:href="@{/main.html}"> Back To Home</a>
        </section>
​
    </div>
</section>
​
<!-- Placed js at the end of the document so the pages load faster -->
<script src="js/jquery-1.10.2.min.js"></script>
<script src="js/jquery-migrate-1.2.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/modernizr.min.js"></script>
​
​
<!--common scripts for all pages-->
<!--<script src="js/scripts.js"></script>-->
​
</body>
</html>

页面如下:

4.9.2)定制错误处理逻辑

  1. 自定义错误页: error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页;

  2. @ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的,新建GlobalExceptionHandler.java ,代码如下:

    /**
     * 处理整个web controller的异常
     */
    @Slf4j
    @ControllerAdvice
    public class GlobalExceptionHandler {
        //处理数学运算异常和空指针异常
        @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
        public String handleArithException(Exception e) {
            log.error("异常是:{}", e);
            //当遇到数学运算异常和空指针异常时跳转到login页面[视图地址]
            return "login";
        }
    }

  3. @ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver :把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);tomcat发送到/error访问错误页, 新建UserTooManyExceptionr.java ,代码如下:

    // 当 http状态码为 HttpStatus.FORBIDDEN时,进入该异常进行处理
    @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "用户数量太多")
    @Slf4j
    public class UserTooManyException extends RuntimeException {
    ​
        public UserTooManyException() {
            log.error("异常是:{用户数量太多}");
        }
    ​
        public UserTooManyException(String message) {
            super(message);
        }
    }

    修改接口内容进行测试:

     @GetMapping("/dynamic_table")
        public String dynamic_table(Model model) {
            //表格内容的遍历
            List<User> users = Arrays.asList(new User("zhangsan", "123456"),
                    new User("lisi", "123444"),
                    new User("haha", "aaaaa"),
                    new User("hehe ", "aaddd"));
    ​
            // @ResponseStatus+自定义异常
            if (users.size() > 3) {
                throw new UserTooManyException();
            }
    ​
            model.addAttribute("users", users);
    ​
            return "table/dynamic_table";
        }

    测试:浏览器访问:http://localhost:8080/dynamic_table,页面如下:

  4. Spring底层的异常:如参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常

    response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());

  1. 自定义实现 HandlerExceptionResolver 处理异常;可以作为默认的全局异常处理规则,新建CustomerHandlerExceptionResolver.java ,代码如下:

    //优先级,数字越小优先级越高,设为最高优先级
    @Order(value = Ordered.HIGHEST_PRECEDENCE)
    @Component
    public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {
        @Override
        public ModelAndView resolveException(HttpServletRequest request,
                                             HttpServletResponse response,
                                             Object handler, Exception ex) {
    ​
            try {
                response.sendError(511, "我喜欢的错误");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return new ModelAndView();
        }
    }

  2. ErrorViewResolver 实现自定义处理异常; ○ response.sendError 。error请求就会转给controller

    ○ 你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controller

    ○ basicErrorController 要去的页面地址是 ErrorViewResolver ;

4.9.3)异常处理自动配置原理

ErrorMvcAutoConfiguration:自动配置异常处理规则

  1. 容器中的组件:类型:DefaultErrorAttributes -> id:errorAttributes

    ■ public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver

    ■ DefaultErrorAttributes:定义错误页面中可以包含哪些数据

  2.  


  1. 容器中的组件:类型:BasicErrorController --> id:basicErrorController(json+白页 适配响应)

■ 处理默认 /error 路径的请求;页面响应 new ModelAndView("error", model); ■ 容器中有组件 View->id是error;(响应默认错误页) ■ 容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。

  1. 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver

■ 如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面 ■ error/404、5xx.html

4.9.4)异常处理步骤流程

  1. 执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException 进行封装,源码如下图:

 

  1. 进入视图解析流程(页面渲染)

源码如下图:

processDispatchResult方法源码如下:

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }
​
        if (mv != null && !mv.wasCleared()) {
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("No view rendering, null ModelAndView returned.");
        }
​
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }
​
        }
    }
  1. 处理handler发生的异常

mv = processHandlerException:处理handler发生的异常,处理完成返回ModelAndView;

  • 遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】,如下图:

 

HandlerExceptionResolver源码如下:

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

  • 系统默认的异常解析器,如下图:

DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;

默认没有任何人能处理异常,所以异常会被抛出:

  1. 如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理

  2. 解析错误视图;遍历所有的 ErrorViewResolver【错误页面解析器】看谁能解析

  3. 默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html

  4. 模板引擎最终响应这个页面 error/500.html

猜你喜欢

转载自blog.csdn.net/jianghao233/article/details/125234552