SpringBoot错误处理底层组件和异常处理流程分析
一、默认规则
默认情况下,Spring Boot提供 /error 处理所有错误的映射
对于机器客户端,它将生成 JSON 响应,其中包含错误,HTTP状态和异常消息的详细信息。
对于浏览器客户端,响应一个 “whitelabel” 错误视图,以HTML格式呈现相同的数据
如果我们自己要自定义错误页样式,可以在 resources/static 或者 resources/templates 下创建一个 error 文件夹,在error里边放咱们自己的错误页面,SpringBoot就可以解析到
二、异常处理自动配置原理
为什么会有这些默认规则呢?肯定是底层的自动配置帮我们配置的。来看一下它在哪里
来自动配置包的 web 文件夹找一下,找到一个 servlet\error 包,这里边就专门保存有关自动处理的一些东西,来到自动配置类 ErrorMvcAutoConfiguration
在这里先大概介绍一下里边都做了什么
首先,它里边的绑定了下面两个配置文件
然后给容器中放了几个组件
组件1:DefaultErrorAttributes,用来定义错误页面中可以包含哪些数据
所以:如果我们想自定义错误页面包含哪些数据,就定义 DefaultErrorAttributes
组件2:BasicErrorController,适配响应 json 还是 白页
点进去看它的作用:
处理默认 /error 路径的请求
页面响应 new ModelAndView(“error”, model);
error其实就对应容器中的一个组件 View,是响应默认错误页
BeanNameViewResolver 也是容器中的组件
这两个组件都是在 WhitelabelErrorViewConfiguration 里边注入的,如下:
串起来就是:我们想要返回一个叫 error 的 ModelAndView ,刚好容器中就有一个 视图叫 error,要按照返回的视图名作为组件的id去容器中找View对象,就用到了 BeanNameViewResolver 这个视图解析器
如果是要响应一个json,就调用的是下边这个
所以:我们要是想自定义页面跳转逻辑,就需要自定义 BasicErrorController
组件3:DefaultErrorViewResolver,自定义错误页面html路径
如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面
如果我们要 自定义错误页面html路径,还可以在配置文件定义 server.error.path=
三、串联异常处理流程
我们给跳转页面接口写一个除零异常,DispatcherServlet 打上断点,模拟异常调试
前边分析过的就不在看了,直接来到执行目标方法的地方
可以看到,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException 封装
出现异常请求结束
然后来到catch捕获异常,拿到异常后也要进入视图解析流程
进入视图解析流程
处理handler发生的异常,处理完成还是返回ModelAndView
那我们来看它是如何处理的
他会遍历所有的 handlerExceptionResolvers,看谁能处理当前异常
我们让它遍历,DefaultErrorAttributes先来
调用 resolveException 方法,在里边保存错误信息
把异常信息保存到rrequest域,并且返回null
返回为null不结束,继续下一个解析器解析
因为我们没有写任何的处理规则,所以遍历完这些解析器,都返回null, 默认没有任何人能处理异常,所以异常会被抛出
抛出去这一次请求就算结束了,我们点放行
如果没有任何人能处理,最终底层就会发送 /error 请求。
然后会被底层的BasicErrorController处理
只有一个,就是我们默认放的 DefaultErrorViewResolver
默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址
然后由模板引擎最终响应这个页面
四、定制错误处理逻辑
经过上边的源码分析,我们总结一下定制错误处理逻辑的几种方式
1、自定义错误页:error/404.html error/5xx.html
前边已经使用过了
2、@ControllerAdvice+@ExceptionHandler处理全局异常;
这种方法 底层是 ExceptionHandlerExceptionResolver 支持的
3、@ResponseStatus+自定义异常
自定义一个异常:UserTooManyException
模拟使用
这种方法 底层是 ResponseStatusExceptionResolver ,把 responsestatus 注解的信息拿到,然后调用 response.sendError(statusCode, resolvedReason);一旦调用了sendError,这次请求就结束了,然后tomcat就会发送 /error
4、自定义实现 HandlerExceptionResolver 处理异常
可以作为默认的全局异常处理规则
呼!错误处理流程分析完毕OVER(∩_∩)O~