• 底层原理分析:探究SpringBoot底层对异常的处理机制


    一. 故事背景

    最近文哥带的学员又要毕业了,其中有个学员在面试时被面试官问到,SpringBoot底层是如何进行异常处理的

    对于底层原理性的东西,文哥认为在学会应用的基础上,适当的去刨根问底,扒拉一下底层,这对提升自己的技术功底是很有好处的。好啦,废话不多说,今天文哥就小试牛刀,带带大家通过对SpringBoot中普通的异常处理,来探究一下SpringBoot底层是如何对异常进行处理的。

    二. SpringBoot异常处理的几个核心组件

    我们要想搞清楚SpringBoot如何进行异常处理,首先要清楚SpringBoot进行异常处理需要用到哪几个组件,以及这些组件的作用是什么。接下来文哥就带你去慢慢分析。

    首先我们可以找到SpringBoot错误处理的自动配置类ErrorMvcAutoConfiguration。

    在ErrorMvcAutoConfiguration里面有几个组件我们需要关注一下。

    1. DefaultErrorAttributes源码剖析

    1.1 DefaultErrorAttributes简介

    我们发现ErrorMvcAutoConfiguration的内部,会向容器注入一个id为errorAttributes的DefaultErrorAttributes类型的组件,那么该组件到底有啥用?我们可以进入源码看看。

    1.2 DefaultErrorAttributes中的方法

    我们再看看DefaultErrorAttributes这个类里面有哪些方法:

    getErrorAttributes方法定义了错误信息,并将错误信息封装到了一个名为errorAttributes的Map集合里面,我们处理错误页面时就可以将这些数据取出并展示出来。

    2. BasicErrorController源码剖析

    2.1 BasicErrorController组件

    在ErrorMvcAutoConfiguration里面还有一个组件BasicErrorController。

    我们可以看看BasicErrorController的具体描述。

    我们发现,当我们要进行错误处理的时候,SpringMVC会默认发送一个/error的请求进行相应的错误处理。

    2.2 其他方法

    我们再看看这个类里面的其他方法:

    我们发现有两个方法,对错误信息进行了响应。

    • errorHtml:当我们使用浏览器发送请求的时候,如果出现了错误,就使用这个方法进行错误信息响应,默认响应的是一个名为error的视图。

    • error:当我们使用其他的方式(比如postman)发送请求的时候,我们就以json格式进行数据响应。默认将数据封装到ResponseEntity里面。

    3. BeanNameViewResolver源码剖析

    3.1 BeanNameViewResolver简介

    在ErrorMvcAutoConfiguration里面还有一个组件BeanNameView Resolver。

    3.2 error视图

    我们看到BeanNameViewResolver是根据视图名称进行解析视图,解析一个名为error的视图。那这个error的视图是什么样的?我们发现源码中return了this.defaultErrorView。

    我们点击进入StaticView类中,发现这样一段代码:

    1. private static class StaticView implements View {
    2.     private static final MediaType TEXT_HTML_UTF8;
    3.     private static final Log logger;
    4.     private StaticView() {
    5.     }
    6.     public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    7.         if (response.isCommitted()) {
    8.             String message = this.getMessage(model);
    9.             logger.error(message);
    10.         } else {
    11.              response.setContentType(TEXT_HTML_UTF8.toString());
    12.              StringBuilder builder = new StringBuilder();
    13.              Object timestamp = model.get("timestamp");
    14.              Object message = model.get("message");
    15.              Object trace = model.get("trace");
    16.              if (response.getContentType() == null) {
    17.                 response.setContentType(this.getContentType());
    18.              }
    19.              builder.append("

      Whitelabel Error Page

      "
      ).append("

      This application has no explicit mapping for /error, so you are seeing this as a fallback.

      "
      ).append("").append(timestamp).append("
      ").append("
      There was an unexpected error (type=").append(this.htmlEscape(model.get("error"))).append(", status=").append(this.htmlEscape(model.get("status"))).append(").
      "
      );
  •              if (message != null) {
  •                 builder.append("
    ").append(this.htmlEscape(message)).append("
    "
    );
  •              }
  •              if (trace != null) {
  •                 builder.append("").append(this.htmlEscape(trace)).append("
    ");
  •              }
  •              builder.append("");
  •              response.getWriter().append(builder.toString());
  •         }
  •     }
  • }
  • 这个render方法就是进行错误页面的具体渲染的地方。

    3.3 render方法

    到这里我们就清楚了,BasicErrorController里面使用errorHtml方法响应错误视图的原理,就是去寻找一个名为error的视图进行解析。而error视图是StaticView类型的,通过里面的render方法进行错误视图渲染的。我们可以看看我们平时出现错误的视图:

    4. DefaultErrorViewResolver源码剖析

    4.1 DefaultErrorViewResolver简介

    在ErrorMvcAutoConfiguration里面还有一个组件DefaultError ViewResolver。

    我们可以点击进入DefaultErrorViewResolver。

    4.2 resolve()方法

    这个类里面还有一个处理错误视图的方法:

    这就回答了为什么我们定义错误页面的时候,为什么要以错误状态码命名,并且将错误页面放在error目录的原因。

    到这里,文哥就把SpringBoot进行错误处理的几个主要的组件介绍清楚了,下一节我们就来详细介绍这几个组件是如何协同工作的。

    三. SpringBoot异常处理核心流程

    上一节中,我们讨论了SpringBoot异常处理所需要的几个核心组件。在接下来的这一节中,我们再来讨论一下SpringBoot进行异常处理的主要流程。

    1. 启动debug模式

    我们可以先启动debugg模式,然后分别在指定的位置打上断点。

    2. 执行请求

    然后发送请求:http://localhost:8080/findById?id=1,这里将断点放行到mv = ha.handle

    3. 继续调试

    我们继续放行断点,如下图所示:

    按下F8继续放行,如下图所示:

    我们发现,如果我们的目标方法出现了异常,会进入catch语句被捕获处理,并且目标方法的返回值ModelAndView为Null。这里我们继续按F8,会如下图所示:

    4. 断点DispatcherServlet

    我们在DispatcherServlet类上面的this.processDispatchResult方法上打断点,并将断点放行。

    此时发现,不管目标方法有没有出现异常,总会被processDis patchResult方法执行(也就是进行视图处理的方法)。我们看它是如何处理的,执行step into,如下图所示:

    我们发现,虽然我们的目标方法出现了异常,返回值mv为null。但是在this.processHandlerException处理目标方法的异常的时候,仍然将处理结果返回成ModelAndView。

    那么这是如何处理的呢?我们继续step into:

    5. 异常处理器工作流程

    我们看看有哪些异常处理器参与工作。

    我们发现有两类异常处理器参与处理工作。一个是Default ErrorAttributes,上一节我们介绍过这个异常处理器,它是SpringBoot默认配置的异常处理器。还有一类是组合异常处理器,有分为三个处理器。我们现在分别使用这4个异常处理器进行处理:

    我们发现所有异常处理器处理之后,都会返回一个ModelAnd View。首先我们看看默认异常处理器DefaultErrorAttri butes如何进行异常处理的,在resolver.resolveException上面step into:

    我们发现这个方法将错误信息封装之后,返回了一个null值。也就是使用DefaultErrorAttributes异常处理器处理之后,处理的结果为Null。

    那么this.storeErrorAttributes又是如何封装异常信息的呢?继续step into:

    我们发现就是将异常信息封装到request作用域。按F8,将代码放行到:

    我们看看HanlderExceptionResolverComposite如何处理的?执行step into:

    这3个异常处理器我们刚刚看到过,如下图所示:

    通过上面代码我们看出也就是分别遍历:

    需要注意的是,这些异常处理器都实现了HandlerException Resolver接口:

    结论:这个三个异常处理器最后处理的结果都为Null,也就是ModelAndView为null。

    6. error方法的处理过程

    此时我们放行断点:

    我们发现当所有的错误异常处理器都不能处理的时候(目标方法的返回值ModelAndView为null),此时默认会转发到/error请求。我们把断点放行到如下位置:

    观察目前处理的是哪个Handler:

    7. errorHtml的处理过程

    我们发现目前请求的是BasicErrorController里面的errorHtml方法。

    BasicErrorController是什么?我们上一节讨论过这个类。里面分别定义了响应html错误页面和json信息的方法。我们断点执行到如下位置:

    我们在BasicErrorController里面的errorHtml方法打上断点,继续放行断点:

    此时会进入BasicErrorController里面的errorHtml方法。

    我们发现在执行this.resolveErrorView方法之前,将所有的错误信息放在了一个Map里面,我们可以看看:

    我们step into进入this.resolveErrorView方法:

    我们发现和之前一样,还是遍历所有的异常处理器进行异常处理,那么到底有哪些异常处理器呢?

    我们发现只有一个异常处理器,那就是DefaultErrorViewResolver。而这个错误视图解析器我们上一节给大家介绍过。

    8. 错误视图的解析过程

    我们看看它是如何进行错误视图解析的,如下图所示:

    我们step into进入resolver.resolveErrorView方法。

    至此,文哥就把SpringBoot默认的异常处理流程给大家介绍完了,希望大家能够从里面汲取精华,对整个SpringBoot的异常处理流程及原理能有深刻的理解。

    *威哥Java学习交流Q群:691533824
    加群备注:CSDN推荐

  • 相关阅读:
    【网盘源码】百度云盘手动cookie获取,添加到扫码系统管理平台。
    这有几个常见的电脑故障解决方法,需要的朋友快来
    二十七、CANdelaStudio深入-编辑技巧(一致性检查)
    AI:61-基于深度学习的草莓病害识别
    帆软的数知鸟是一个什么东西
    spring高级源码50讲-20-36(springMVC)
    kafka3.6.0集群部署
    一些个人电脑用的小工具软件
    【毕业季】平凡的求学生涯,回顾与启程
    牛客多校 1 (字典序 模拟 几何 斜率 启发式合并+缩边 期望dp )
  • 原文地址:https://blog.csdn.net/finally_vince/article/details/126031394