Spring Boot-15-异常处理流程

上文中了解了 Spring Boot 的异常报告器的工作原理,但是并没有看到他的调用时机, 其实就是在 run 方法的 catch 部分的 handleRunFailure() 中做处理,下面我们来看一下这个方法的具体处理流程

源码跟踪

首先还是进入 run 方法,进到 catch 块中,找到 handleRunFailure() 这个方法,如下

image

之后进到这个方法看一下

image

这个方法一共做了五件事:

  1. 处理退出状态码
  2. 发送失败事件
  3. 错误报告
  4. 关闭 context
  5. 把异常重新抛出去

一个一个看一下这几件事情的具体处理流程

状态码处理

首先进入 handleExitCode() 这个方法看一下

image

这个里面调用了 getExitCodeFromException() 去获取状态码,点进去继续往下看

image

具体的计算方法如下

image

总体来说,就是找到 ExitCodeExceptionMapper 的实现类,然后根据具体的错误,找到对应的状态码, 0是正常退出, 否则为非正常退出

发送失败事件

状态码处理完毕后,判断了 listeners 是否为空,然后发送了一个失败事件, 判空原因主要是防止监听器还没加载完毕,程序就出错了,具体发送事件的原理这里就不分析了,可以回头到监听器的那里去看

错误报告

接着往下看一下 reportFailure() 错误报告的这个方法

image

这里先去 exceptionReporters 集合,其实就我们上文中介绍的 FailureAnalyzers 这一个类, 当然我们也可以手动写一个 SpringBootExceptionReporter 的实现,但是这里的 reportException() 返回 true 的话,就不会进入下一个了,具体的报告流程我们在上文中详细分析过了

关闭 context

接着往下看,调用了 context.close() 这方法,点进去,看看具体做了哪些事情

image

这里先调用了 doClose() 方法, 然后把钩子方法移除掉, 钩子方法就是在 jvm 退出的时候做的一些事情, 然后我们来关心一下 doClose() 这个方法

image

重新抛出异常

最后一步会调用 ReflectionUtils.rethrowRuntimeException(exception) 这个方法把异常重新抛出去,这里点进去看一下

image

这里先把异常做个包装,然后再抛出去,以上就是全部的异常处理流程

总结

handleExitCode() 作用:

  • 计算退出状态码,为0是正常退出,非0则是异常退出
  • 发送 ExitCodeEvent 事件
  • 记录 exitCode 的值

listeners.failed() 作用:

  • 发送 ApplicationFailedEvent 事件

reportFailure() 作用:

  • 调用异常报告器, 成功的话记录已处理的异常

context.close() 作用:

  • 更改应用上下文状态
  • 销毁单例Bean
  • 将 beanFactory 置空
  • 关闭web容器(web环境中)
  • 移除 shutdownHook 钩子方法

ReflectionUtils.rethrowRuntimeException(exception) 作用:

  • 包装异常,重新抛出

钩子方法 (shutdownHook) 介绍:

  • jvm 退出的时候执行的业务逻辑
  • 通过 Runtime.getRuntime().addShutdownHook() 方法添加
  • 通过 Runtime.getRuntime().removeShutdownHook() 方法移除