SpringBoot之全局异常处理

默认情况下的异常现象

创建一个接口 (接口需要传递参数key)
@RestController
@RequestMapping("/exception")
public class ExceptionController {@GetMapping("/accept")public String acceptKey(@RequestParam("key") String key) {return key;}
}
访问链接(不传递参数key,使得抛出异常) http://localhost:8080/exception/accept
在浏览器中的现象

在 Postman 中的现象

小结

在浏览器中返回一个 html 页面,在 Postman 中返回一个 json 数据

解决方案

在默认静态资源路径下创建 error 子文件夹,并创建文件 400.html

默认静态资源路径如下:

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/

400.html 明细如下所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>404</h1>
</body>
</html>

再次在浏览器下访问链接 : http://localhost:8080/exception/accept

PS:错误码需要和 html 名字一致,或者将 html 改成 4xx、5xx,这样以 4 开头的错误码就会跳转到 4xx.html,以 5 开头的错误码就会跳转到 5xx.html

再次在 Postman 中访问链接 : http://localhost:8080/exception/accept

好像并没有起作用,我们再尝试其他方法

创建 GlobalExceptionHandler,处理全局异常
创建实体类 ExceptionInfo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionInfo {private String msg;
}
创建全局异常处理配置类 GlobalExceptionHandler
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)@ResponseBodypublic ExceptionInfo resolverException(Exception exception) {ExceptionInfo exceptionInfo = new ExceptionInfo();exceptionInfo.setMsg(exception.getMessage());return exceptionInfo;}
}
访问链接 : http://localhost:8080/exception/accept

源码解析

ErrorMvcAutoConfiguration

ErrorMvcAutoConfiguration 会定义一个类型为 BasicErrorController 的  Bean。BasicErrorController 中定义了两个接口方法:errorHtmlerror ,其中 errorHtml 方法只对 request 的 accept 能兼容 text/html 的请求有效,error 方法则可以认为是一个兜底方法,它对 request 的 accept 没有要求

当我们的请求抛出异常,会转发到这个接口(/error),如果这个默认的 URI(/error) 和我们的项目有冲突,我们可以在配置文件中定义 server.error.path | error.path 来修改默认值。

StandardHostValve#custom (请求转发)

DispatcherServlet#processDispatchResult

我们需要关注两个方法 processHandlerExceptionrender

case1:访问一个不存在的接口或者不存在的文件

这种情况 exception 和 mv(ModelAndView)都为 null,所以既不会执行 processHandlerException 方法,也不会执行 render 方法,然后转发到 /error 接口。

在 Postman 中发请求,Accept 默认为 */*,在浏览器中发请求 Accept  如下所示:

根据一定的规则,在 Postman 中默认转发到 error 方法,在浏览器中默认转发到 errorHtml 方法

AbstractHandlerMethodMapping#lookupHandlerMethod (请求映射规则)

case1.1 转发到 error 方法

该方法会构建一个 map 对象,设置 timestampstatuserrorpath 等信息并响应

case1.2 转发到 errorHtml 方法

默认情况下 errorViewResolvers 只有一个,类型为 DefaultErrorViewResolver,它是在 ErrorMvcAutoConfiguration 内部类 DefaultErrorViewResolverConfiguration 中定义的,相关源码如下:

DefaultErrorViewResolver#resolveErrorView

当我们访问一个不存在的链接,viewName 为  404,errorViewName 为 error/404,如果 TemplateAvailabilityProviders 的 getProvider 方法返回一个非 null 对象,则返回一个 ModelAndView 对象

TemplateAvailabilityProviders#getProvider

如果 TemplateAvailabilityProvider 的 isTemplateAvailable 方法返回 true,则返回当前 TemplateAvailabilityProvider 对象,即最终会返回一个 ModelAndView 对象

根据 SpringBoot 的自动配置,容器中存在五个 TemplateAvailabilityProvider ,我们来看一下 ThymeleafTemplateAvailabilityProvider 的 isTemplateAvailable 方法。

即默认情况下,如果我们的环境中,存在指定的类(org.thymeleaf.spring5.SpringTemplateEngine),并且资源 classpath:/templates/error/404.html 存在,则返回一个 ModelAndView 对象

可以导入下方所示的依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId><version>2.6.13</version>
</dependency>

DefaultErrorViewResolver#resolveResource

如果 TemplateAvailabilityProviders 的 getProvider 方法返回 null,则继续调用 resolveResource 方法。该方法会遍历 staticLocations,判断这些默认静态文件路径下是否存在相关文件(是否存在error/404.html),如果存在则构建一个 HtmlResourceView 对象,staticLocations 明细列表如下:

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/

DefaultErrorViewResolver#resolveErrorView (2)

如果错误状态码是以 4 或 5 开头,则以 viewName 为  4xx,errorViewName 为 error/4xx,再执行一遍上述的流程

BasicErrorController#errorHtml(2)

即不管我们有没有在当前环境中找到指定文件,都会返回一个 ModelAndView 对象,如果存在以下资源,viewName 则不为 error:

  1. classpath:/templates/error/404.html  => view:error/404
  2. classpath:/META-INF/resources/404.html => view:HtmlResourceView
  3. classpath:/resources/404.html  => view:HtmlResourceView
  4. classpath:/static/404.html  => view:HtmlResourceView
  5. classpath:/public/404.html  => view:HtmlResourceView
  6. classpath:/templates/error/4xx.html => view:error/4xx
  7. classpath:/META-INF/resources/4xx.html => view:HtmlResourceView
  8. classpath:/resources/4xx.html => view:HtmlResourceView
  9. classpath:/static/4xx.html => view:HtmlResourceView
  10. classpath:/public/4xx.html => view:HtmlResourceView
  11. 其他 => view:error

DispatcherServlet#processDispatchResult(2)

第二次进入 DispatcherServlet 的 processDispatchResult 方法,此时 mv 不等于null,则进入render 方法

如果 viewName 为 error/404、error/4xx、error 则执行 resolveViewName 方法

一共有5个resolver,我们只需要关注 ContentNegotiatingViewResolver 的 resolveViewName 方法

ContentNegotiatingViewResolver 的内部属性 viewResolvers 有其他四个 resolvers,即 ContentNegotiatingViewResolver 相当于一个大管家,具体还是由其他四个 resolvers 处理,我们简要分析一下 beanNameViewResolver

ErrorMvcAutoConfiguration 的内部类 WhitelabelErrorViewConfiguration 会定义两个bean,其中一个 beanName 为 error,类型为 View,另一个 beanName 为 beanNameViewResolver,类型为 BeanNameViewResolver

即 BeanNameViewResolver 的 resolveViewName 方法会返回一个类型为 StaticView 的 View

View#render

StaticView#render

我们可以看到 StaticView 的 render 方法就是我们经常看到的页面

StaticView#render

HtmlResourceView#render

HtmlResourceView 的 render 方法就是将指定资源用流写出去

case2:访问一个存在的接口且抛出异常
DispatcherServlet#processHandlerException

一共有两个 HandlerExceptionResolver,其中一个类型为 DefaultErrorAttributes,DefaultErrorAttributes 的 resolveException 方法比较简单,主要是给 request 赋值,我们主要关注 HandlerExceptionResolverComposite 的 resolveException 方法

HandlerExceptionResolverComposite#resolveException

一共有三个 HandlerExceptionResolver,我们主要分析一下 ExceptionHandlerExceptionResolver的 resolveException 方法

ExceptionHandlerExceptionResolver#resolveException

最终会执行类上标记 @ControllerAdvice 注解,方法上标记 @ExceptionHandler 注解的符合条件的方法,就是我们在【解决方案】的 GlobalExceptionHandler#resolverException 方法

如何选择 ServletInvocableHandlerMethod

遍历 exceptionHandlerAdviceCache 对象,通过 ExceptionHandlerMethodResolver 的resolveMethod 方法,获取一个 method 对象,并将其封装成 ServletInvocableHandlerMethod 对象

isApplicableToBeanType

以下四种情况,@ControllerAdvice注解生效:

  1. 什么都没有配置
  2. Controller所在的类路径以配置的 basePackages 开头
  3. Controller类型是指定的类或是其子类
  4. Controller上含有指定注解

resolveMethod

遍历 mappedMethods 对象,如果存在多个 @ExceptionHandler 标注的方法,则选择一个优先级最高的

ExceptionHandlerExceptionResolver 的 exceptionHandlerAdviceCache 是如何赋值的

ExceptionHandlerExceptionResolver 继承 InitializingBean 接口,所以 bean 在实例化的过程中会执行其 afterPropertiesSet 方法

ExceptionHandlerExceptionResolver#afterPropertiesSet

如果 bean 上存在 @ControllerAdvice 注解,则构建一个 ControllerAdviceBean 对象

循环遍历查找出来的 adviceBeans,每存在一个 ControllerAdviceBean,则构建一个ExceptionHandlerMethodResolver 并将其 put 到 exceptionHandlerAdviceCache

ExceptionHandlerMethodResolver的实例化(给 mappedMethods 赋值)

如果方法存在 @ExceptionHandler 注解,则给 mappedMethods 赋值

PS : 如果 @ExceptionHandler 注解标注的方法也抛出异常,则使用 case1 做兜底

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47848.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言第5天作业 7月16日

目录 1.求1000以内所有的质数。 2.有1、2、3、4个数字&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;都是多少&#xff1f; 3.猴子吃桃问题 4.判断最大值 1.求1000以内所有的质数。 质数&#xff1a;只能够1和它本身整除 #include <stdio.h> in…

Camera Raw:首选项

Camera Raw 首选项 Preferences提供了丰富的配置选项&#xff0c;通过合理设置&#xff0c;可以显著提升图像处理的效率和效果。根据个人需求调整这些选项&#xff0c;有助于创建理想的工作环境和输出质量。 ◆ ◆ ◆ 打开 Camera Raw 首选项 方法一&#xff1a;在 Adobe Bri…

Linux系统学习日记——vim操作手册

Vim编辑器是linux下的一个命令行编辑器&#xff0c;类似于我们windows下的记事本。 目录 打开文件 编辑 保存退出 打开文件 打开 hello.c不存在也可以打开&#xff0c;保存时vim会自动创建。 效果 Vim打开时&#xff0c;处于命令模式&#xff0c;即执行命令的模式&#x…

VUE中setup()

在Vue中&#xff0c;setup() 函数是Vue 3.0及更高版本引入的一个重要特性&#xff0c;它是Composition API的入口点。setup() 函数用于初始化组件的状态和逻辑&#xff0c;包括定义响应式数据、方法和生命周期钩子。以下是关于setup() 函数的详细解释&#xff1a; 1. 作用与特…

解决IDEA 中出现已有类、函数找不到的情况

缓存导致部分索引失效&#xff0c;需要刷新缓存并重启idea即可 1、File > Invalidate Cache / Restart... 2、Invalidate and Restart

C# 4.List

comboBox使用的下拉框 Lsit 列表 1 创建List对象 List<string> list new List<string>(); 2 Add给list 添加元素 list.Add("吃饭"); list.Add("睡觉"); list.Add("打豆豆"); 3 删除一个元素 list.Remove("吃饭"); // 删…

【精简版】jQuery 中的 Ajax 详解

目录 一、概念 二、jQuery 发送 GET 请求 三、jQuery 发送 POST 请求 四、$.ajax() 方法 1、含义 2、settings 选项 ① type 属性 ② async 属性 ③ headers 属性 ④ contentType 属性 ⑤ processData 属性 ⑥ data 属性 ⑦ timeout 属性 ⑧ beforeSend(jqXHR) 方…

聊聊常见的分布式ID解决方案

highlight: xcode theme: vuepress 为什么要使用分布式ID&#xff1f; 随着 Web 开发技术的不断发展&#xff0c;单体的系统逐步走向分布式系统。在分布式系统中&#xff0c;使用分布式 ID(Distributed IDs)主要是为了在没有单点故障的情况下生成唯一标识符。这些唯一标识符在很…

C++【OpenCV】图片亮度色度归一化

#include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <iostream>using namespace cv; using namespace std;int main() {Mat image imread("SrcMF.jpg");// 灰度、Gamma归一化亮度cv::Mat m_gray;cv::cvtColor(image, m_gra…

Linux-CentOS7忘记密码找回步骤

虚拟机版本 一、进入开机页面&#xff0c;先按上下&#xff08;↑↓&#xff09;键&#xff0c;以免系统自动启动。 二、按“e”键进入编辑页面,找到如下图位置&#xff0c;输入&#xff1a;init/bin/sh 按CTRLX 进入单用户模式。 三、 输入 mount -o remount,rw / 然后按 ent…

为什么局部内部类和匿名内部类只能访问局部final变量?

简述final final修饰的类表示不可被继承&#xff0c;修饰方法表示此方法不可以被子类覆盖&#xff0c;但是可以被重载&#xff0c;修饰变量表示变量一旦被赋值就不可以更改它的值&#xff08;基础类型是值&#xff0c;引用类型是地址&#xff09; 修饰成员变量 a. 如果final修…

【ARMv8/v9 GIC- 700 系列 2 -- GIC-700 上电控制寄存器 GICR_PWRR】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 GIC-700 上电GICR_PWRR 寄存器字段介绍GICR_PWRR 功能说明GICR_PWER 代码配置GICR_PWRR 使用场景GICR_PWRR 注意事项GIC-700 上电 GICR_PWRR(功耗寄存器)是ARM GICv4架构中用于控制GIC-700是否可以关闭电源的寄存器。它通过几个位…

sql server插入数据或查询慢如何诊断?

SQL Server插入数据慢的原因可能有多种&#xff0c;具体原因需要根据实际情况进行分析。以下是一些常见的原因及解决办法&#xff1a; 1. 索引问题 大量索引&#xff1a;在插入数据时&#xff0c;SQL Server需要更新所有相关的索引&#xff0c;这会增加插入操作的开销。 解决…

git-常用基础指令

一、基本指令 1. 配置用户名和邮箱 git config --global user.name "Your Name" git config --global user.email "your.emailexample.com"2. 初始化仓库 git init3. 克隆仓库 git clone <repository_url>4. 查看当前状态 git status5. 添加文件…

iPython与Matplotlib:数据可视化的秘籍

iPython与Matplotlib&#xff1a;数据可视化的秘籍 前言 欢迎来到"iPython与Matplotlib&#xff1a;数据可视化的秘籍"教程&#xff01;无论你是数据可视化新手还是希望提升技能的专业人士&#xff0c;这里都是你开始的地方。让我们开始这段数据可视化之旅吧&#…

C++:opencv将彩色图转换为灰度图

在C中&#xff0c;使用OpenCV库将彩色图转换成灰度图是一个相对直接的过程。你可以使用cv::cvtColor()函数&#xff0c;该函数可以将图像从一个颜色空间转换到另一个颜色空间。对于将彩色图转换为灰度图&#xff0c;你通常会从BGR颜色空间&#xff08;OpenCV中默认的彩色图像格…

Go语言并发编程-Goroutine调度

goroutine 概念 在Go中&#xff0c;每个并发执行的单元称为goroutine。通常称为Go协程。 go 关键字启动goroutine go中使用关键字 go 即可启动新的goroutine。 示例代码&#xff1a; 两个函数分别输出奇数和偶数。采用常规调用顺序执行&#xff0c;和采用go并发调用&…

Go 1.19: 性能提升与新特性

Go语言&#xff0c;以其简洁、高效和并发支持而广受欢迎。随着Go 1.19版本的发布&#xff0c;Go语言再次为开发者带来了一系列新特性和改进。本文将介绍Go 1.19的主要变化、新特性以及如何开始使用这个新版本。 Go 1.19 简介 Go 1.19是Go语言的一次重大更新&#xff0c;它在性…

OpenCV教程:cv2如何把两张图片的大小,设置成相同的宽高

-------------OpenCV教程集合------------- Python教程99&#xff1a;一起来初识OpenCV&#xff08;一个跨平台的计算机视觉库&#xff09; OpenCV教程01&#xff1a;图像的操作&#xff08;读取显示保存属性获取和修改像素值&#xff09; OpenCV教程02&#xff1a;图像处理…

前端开发实际项目中常见的实用技巧

CSS filter .logo1 {/* 设置阴影 */filter: drop-shadow(1px 1px 10px #333); } .title {/* 设置模糊度,值越大,越模糊*/filter: blur(1px); } .logo2 {/* 设置对比度,值越大,对比度越大 */filter: contrast(10); } .logo3 {/* 设置制灰程度,常用于一些特殊日子 */filter…