springmvc 全局异常处理器配置的三种方式深入底层源码分析原理

文章目录

  • springmvc 全局异常处理器配置的三种方式&深入底层源码分析原理
    • 配置全局异常处理器的三种方式
      • 实现接口HandlerExceptionResolver并配置到WebMvcConfigurer
      • 注解式配置@ExceptionHandler
      • controller里方法上定义@ExceptionHandler
    • 深入源码分析
      • 进入DispatcherServlet
      • 执行handler方法并catch异常

springmvc 全局异常处理器配置的三种方式&深入底层源码分析原理

配置全局异常处理器的三种方式

实现接口HandlerExceptionResolver并配置到WebMvcConfigurer

定义一个类实现这个接口


/*** Interface to be implemented by objects that can resolve exceptions thrown during* handler mapping or execution, in the typical case to error views. Implementors are* typically registered as beans in the application context.** <p>Error views are analogous to JSP error pages but can be used with any kind of* exception including any checked exception, with potentially fine-grained mappings for* specific handlers.** @author Juergen Hoeller* @since 22.11.2003*/
public interface HandlerExceptionResolver {/*** Try to resolve the given exception that got thrown during handler execution,* returning a {@link ModelAndView} that represents a specific error page if appropriate.* <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}* to indicate that the exception has been resolved successfully but that no view* should be rendered, for instance by setting a status code.* @param request current HTTP request* @param response current HTTP response* @param handler the executed handler, or {@code null} if none chosen at the* time of the exception (for example, if multipart resolution failed)* @param ex the exception that got thrown during handler execution* @return a corresponding {@code ModelAndView} to forward to,* or {@code null} for default processing in the resolution chain*/@NullableModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);}

定义一个配置类实现接口 WebMvcConfigurer 并重写方法configureHandlerExceptionResolvers将你定义的这个resolver加入到resolvers这个List即可

注解式配置@ExceptionHandler

要让spring扫描到这个类,一种方式是直接把这个类放到springboot能扫描的目录下(启动类当前目录或者子目录,或者在启动类手动配置的扫描组件目录),另一种方式是放在工具类里,定义到spring.factories里让spring扫描到,注意设置个@Order(0)让这个优先于springmvc注解错误处理器之前注入到容器,否则会无效,因为那个处理器只加载一次,后续不会再扫容器里的实例了。

/*** @author humorchen* date: 2024/6/11* description: 认证全局异常配置**/@Slf4j
@RestControllerAdvice
@Order(0)
public class AuthExceptionResolver {public static final int TOKEN_ERROR_CODE = HttpStatus.HTTP_UNAUTHORIZED;public static final String ERROR_MSG = "未登录或登录已失效!";/*** TOKEN相关异常** @param e* @return*/@ExceptionHandler({SaTokenException.class, JWTVerificationException.class})public Result<String> resolveAuthTokenException(Exception e) {log.info("handleSaTokenException {}: {}", e.getClass().getName(), e.getMessage());return Result.fail(TOKEN_ERROR_CODE, ERROR_MSG);}}

看到上面代码,你会有一个疑问,要是定义了多个,它怎么确定该用哪个呢,源码中使用ExceptionDepthComparator排序,排序规则是按照继承顺序来(继承关系越靠近数值越小,当前类最小为0,顶级父类Throwable为int最大值),排序之后选取继承关系最靠近的那个,并且ExceptionHandlerMethodResolver的exceptionLookupCache中,key为当前抛出的异常,value为解析出来的匹配method.
org.springframework.core.ExceptionDepthComparator
在这里插入图片描述

controller里方法上定义@ExceptionHandler

@RestController
public class ExceptionController {@ExceptionHandler(Exception.class)public ResponseEntity<String> handleException(Exception ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred: " + ex.getMessage());}@RequestMapping("/test")public String test() throws Exception {throw new Exception("Test exception!");}
}

深入源码分析

进入DispatcherServlet

在这里插入图片描述

执行handler方法并catch异常

在这里插入图片描述
调用processDispatchResult处理分发处理结果
在这里插入图片描述

发现有异常则调用processHandlerException 处理handler异常
在这里插入图片描述

如果有handlerExceptionResovlers就循环调用,直到有一个resolver返回了非空值就跳出循环
在这里插入图片描述
默认resolver,就是把这个异常放到了request.attribute里头去了

在这里插入图片描述

在这里插入图片描述

HandlerExceptionResolverComposite,是handler异常处理器的复合类,把这些resolver放到它内部一个集合
在这里插入图片描述
在这里插入图片描述
点开这个类源码看下,其实就是在我们在webmvcconfigurer里配置这个方法的时候,把配置的所有resolver设置进这个类里的this.resolvers 集合去了,进调试可以看到有两个resolver,一个是我自定义并配置上的ExceptionResolver,另一个是个Http请求异常处理器(seata的HttpHandlerExceptionResolver)
在这里插入图片描述

在这里插入图片描述
分布式事务用来清理ID的,不用管

在这里插入图片描述
第三个也是我注入的那个错误处理器
如果经过这些处理器,有一个返回了非空的结果,这个异常 处理就会被结束

在这里插入图片描述
执行完所有的resolver,有一个返回了ModelAndView的话就返回这个ModelAndView,否则继续抛出异常往上层传递。然后触发请求handler的triggerAfterCompletion,也就是执行你所有跟这个handler绑定的HandlerInterceptor的afterCompletion

在这里插入图片描述

前面看的都是HandlerExceptionResolver的实现,还有那个注解式的实现,我们也一起看下这个ExceptionHandlerExceptionResolver 类的源码,就是用来处理 @ExceptionHandler的

/*** An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions* through {@code @ExceptionHandler} methods.** <p>Support for custom argument and return value types can be added via* {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.* Or alternatively to re-configure all argument and return value types use* {@link #setArgumentResolvers} and {@link #setReturnValueHandlers(List)}.** @author Rossen Stoyanchev* @author Juergen Hoeller* @since 3.1*/
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolverimplements ApplicationContextAware, InitializingBean {@Nullableprivate List<HandlerMethodArgumentResolver> customArgumentResolvers;@Nullableprivate HandlerMethodArgumentResolverComposite argumentResolvers;@Nullableprivate List<HandlerMethodReturnValueHandler> customReturnValueHandlers;@Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;private List<HttpMessageConverter<?>> messageConverters;private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();private final List<Object> responseBodyAdvice = new ArrayList<>();@Nullableprivate ApplicationContext applicationContext;private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =new ConcurrentHashMap<>(64);private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =new LinkedHashMap<>();

在这个bean执行到afterPropertiesSet这个时间点的时候进行了 @ExceptionHandler实现的扫描initExceptionHandlerAdviceCache(),如果你的 @ExceptionHandler配置bean在这个时间点之后注入进IOC容器的话,那么恭喜你,你的配置不会生效,因此前面我说要让自己的这个配置优先级变高,才能生效。
在这里插入图片描述
ControllerAdviceBean.findAnnotatedBeans(getApplicationContext())
把所有的标注了@ControllerAdvice注解的bean全部找出来并根据order排序返回了

在这里插入图片描述

在这里插入图片描述

看到这你会疑问还有个@RestControllerAdvice,来看源码,@RestControllerAdvice上标注了@ControllerAdvice
在这里插入图片描述
前面已经看了加载过程,现在看执行处理异常的代码,其实就是拿着加载好的处理器,去调用处理,然后参数呢又是可选的,随便你定义,那个其实是反射拿到你方法定义的参数,然后跟现有能给你的一些参数比对类型,然后组成一个参数数组,传入执行对象、参数反射执行方法,然后就拿到了异常处理结果

在这里插入图片描述
组装生成调用参数数组的方法就在这org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
在这里插入图片描述
也就是把异常对象和当前的handlerMethod给你了,你自己可以写到参数里它会注入给你的
在这里插入图片描述

在这里插入图片描述

ExceptionHandler原理另一个作者也写的不错:
从源码角度去深入分析关于Spring的异常处理ExceptionHandler的实现原理

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

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

相关文章

SpringBoot3 常用的第三方接口调用十种方式

环境&#xff1a;SpringBoot.3.3.0 简介 在项目中调用第三方接口是日常开发中非常常见的。调用方式的选择通常遵循公司既定的技术栈和架构规范&#xff0c;以确保项目的一致性和可维护性。无论是RESTful API调用、Feign声明式HTTP客户端、Apache HttpClient等调用方式&#x…

经典的带环链表问题(链表补充)

环形链表1 运用快慢指针的方法&#xff0c;fast ,slow从头节点出发&#xff0c;快指针走两步&#xff0c;慢指针走一步&#xff0c;若有环&#xff0c;快指针先进环&#xff0c;后续如果慢指针和快指针相遇&#xff0c;则链表带环。转换成了追击问题。 struct ListNode {int v…

重温react-01

创建react项目 // 第一步 npm install create-react-app -g // 第二步 create-react-app my-app目录介绍 my-app/README.md# 项目第三方依赖包node_modules/package.json# 一般用来存放静态依赖public/index.htmlfavicon.ico# 存放项目源代码&#xff0c;注意只有放在scr目录…

快速提升沟通能力:客服必备的话术技巧

在现在的这个互联网时代&#xff0c;各行业竞争日益激烈&#xff0c;而客服作为连接商家和消费者的桥梁&#xff0c;无疑是一个重要的岗位。可以说客服是一个极具挑战性的岗位&#xff0c;客服每天需要面对来自全国各地的客户&#xff0c;同时还要对不同地区、不同性格、不同需…

SQLServer 借助Navcate做定时备份的脚本

首先创建SQLServer链接&#xff0c;然后在Query标签种创建一个查询 查询内容如下 use ChengYuMES declare ls_time varchar(1000) declare ls_dbname varchar(1000) set ls_time convert(varchar, getdate(), 112) _ replace(convert(varchar, getdate(), 108), :, )-- 需…

LeetCode20.有效的括号

题目描述 分析 我们刚上来的思路可能是&#xff1a;找出这三种括号的个数 如果都是偶数 说明匹配 但是这里还有一个顺序问题 比如 " )( "这样是不匹配的&#xff01; 所以这种思路不可取&#xff01; 我们想 如果遇到左括号&#xff0c;把他读到一个顺序表中&#…

Redis应用:基于Redis实现排行榜、点赞、关注功能

文章目录 1. 环境准备2. 实现排行榜功能2.1 添加用户分数2.2 获取排行榜2.3 获取用户排名2.4 更新用户分数示例代码3. 实现点赞功能3.1 添加点赞3.2 获取点赞数3.3 检查用户是否点赞3.4 取消点赞示例代码4. 实现关注功能4.1 添加关注4.2 获取粉丝列表4.3 获取关注列表4.4 取消关…

【Pycharm】设置双击打开文件

概要 习惯真可怕。很多小伙伴用习惯了VsCode开发&#xff0c;或者其他一些开发工具&#xff0c;然后某些开发工具是单击目录文件就能打开预览的&#xff0c;而换到pycharm后&#xff0c;发现目录是双击才能打开预览&#xff0c;那么这个用起来就特别不习惯。 解决办法 只需一…

高清视频+AI算法,EasyCVR视频智能监控方案打造无死角吸烟行为检测

一、背景与意义 1、吸烟危害&#xff1a;吸烟不仅有害健康&#xff0c;而且在特定场所带来的安全隐患极大。据统计&#xff0c;全年火灾事故中有五分之一系抽烟引起&#xff0c;引发的人员伤亡和财产损失巨大。 2、政策与法规&#xff1a;为了保护公共安全&#xff0c;消除消…

解决方案︱视频孪生智慧高速解决方案

系统概述 在交通强国战略的指导下&#xff0c;我国政府高度重视以数字化为核心的智慧高速公路建设与发展。2023年9月&#xff0c;交通运输部印发了《交通运输部关于推进公路数字化转型加快智慧公路建设发展的意见》&#xff0c;强调到2035年&#xff0c;全面实现公路数字化转型…

java多线程概念

在Java多线程编程中有几个重要的概念&#xff0c;这些概念对于理解和编写正确的多线程应用程序至关重要&#xff1a; 1.线程&#xff08;Thread&#xff09;: 线程是操作系统能够进行运算调度的最小单位&#xff0c;Java通过线程实现并发执行。 2.进程&#xff08;Process&…

助力OTT大屏营销,酷开科技引领产业变革与创新

随着大屏电视产品的迭代&#xff0c;越来越多家庭以增换购等多种形式获得超高清、超大屏的智能电视&#xff0c;大屏的人均拥有量和渗透率进一步增加。在这种情况下&#xff0c;通过OTT应用为载体&#xff0c;将大量内容持续输送到大屏终端&#xff0c;从而形成了电视硬件普及与…

工控机与普通电脑的区别对于工业自动化应用至关重要

商用计算机和工业计算机之间的相似之处可能多于差异之处。工业电脑利用了消费技术领域的许多进步&#xff0c;但增加了工业应用所必需的软件、编程、确定性和连接性。 专业人士表示&#xff1a;“从增加内存到摩尔定律所描述的处理能力的指数级增长&#xff0c;工业控制必将受…

【C++】和【预训练模型】实现【机器学习】【图像分类】的终极指南

目录 &#x1f497;1. 准备工作和环境配置&#x1f495; &#x1f496;安装OpenCV&#x1f495; &#x1f496;安装Dlib&#x1f495; 下载并编译TensorFlow C API&#x1f495; &#x1f497;2. 下载和配置预训练模型&#x1f495; &#x1f496;2.1 下载预训练的ResNet…

pip 配置缓存路径

在windows操作平台&#xff0c;默认情况&#xff0c;pip下使用的系统目录 C:\Users\用名名称\AppData\Local\pip C盘是系统盘&#xff0c;如果常常使用pip安装会占用大量的空间很快就满&#xff0c;这时候就有必要变更一下缓存保存路径了。 pip 配置缓存路径&#xff1a; Win…

FM全网自动采集聚合影视搜索源码

源码介绍 FM 全网聚合影视搜索(响应式布局)&#xff0c;基于 TP5.1 开发的聚合影视搜索程序&#xff0c;本程序无数据库&#xff0c;本程序内置P2P 版播放器&#xff0c;承诺无广告无捆绑。片源内部滚动广告与本站无关,谨防上当受骗&#xff0c;资源搜索全部来自于网络。 环境…

效率翻倍!ComfyUI 必装的工作流+模型管理插件 Workspace Manager

一、Workspace Manager 安装方式 插件 Github 网址&#xff1a; https://github.com/11cafe/comfyui-workspace-manager 如果你没有安装 Workspace Manager 插件&#xff0c;可以通过以下 2 种方式安装&#xff1a; ① 通过 ComfyUI Manager 安装&#xff08;推荐&#xff0…

基于python-CNN卷积网络训练识别牛油果和猕猴桃-含数据集+pyqt界面

代码下载地址&#xff1a; https://download.csdn.net/download/qq_34904125/89383066 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文…

LeetCode | 35.搜索插入位置

这套题可以直接遍历&#xff0c;找到第一个大于target的数并返回其位置即可&#xff0c;但是时间复杂度为 O ( n 2 ) O(n^2) O(n2)&#xff0c;题目中明确要求时间复杂度为 O ( l o g n ) O(logn) O(logn)&#xff0c;考虑二分查找算法&#xff0c;这道题就是标准的二分查找的一…

android 播放视频

播放视频文件 新建一个activity_main.xml文件&#xff0c;文件中放置了3个按钮&#xff0c;分别用于控制视频的播放、暂停和重新播放。另外在按钮的下面又放置了一个VideoView&#xff0c;稍后的视频就将在这里显示。 <LinearLayout xmlns:android"http://schemas.an…