所以spring mvc异常处理工作原理是啥

文章目录

  • spring mvc异常处理(源码分析)
    • 概述
    • 原理(源码角度)模拟debug
      • 前期提要
      • 分析
      • 4个map
      • 4个map的初始化
      • 为什么需要基于mappedMethods缓存
    • 总结一下

spring mvc异常处理(源码分析)

概述

spring mvc有下面三种方式实现异常处理:

分别是:

  • 实现handlerExceptionResolver+@Component(上古版本)
  • controller里耦合@ExceptionHandler(优先级最高)
  • @ControllerAdvice+@ExceptionHandler(最常用)

1.在对应类实现spring的异常处理核心组件handlerExceptionResolver+@Component(在多个异常执行时的优先级最低,并且麻烦,最早期的异常处理)

在这里插入图片描述

@ExceptionHandler注解在controller方法上(优先级高于ControllerAdvice,但比较麻烦)

在这里插入图片描述

@ControllerAdvice+@ExceptionHandler统一异常处理(最常用)

在这里插入图片描述

原理(源码角度)模拟debug

从源码角度分析,spring mvc是如何进行统一异常处理的?

为了更好的说明问题,我选择用倒推,先去模拟错误发生

前期提要

1.我定义了一个controller,有一个deleteBook方法,在这个方法中会对传来的bookId检查,非法则会抛出一个runtimeException

在这里插入图片描述

2.我用上述三种spring mvc异常处理方法都加在了项目中:如概述的图片所示

3.向deleteBook发送一个id非法的请求。

4.在processHandlerException方法处打上断点。

分析

可以看到这里马上去执行resolveException(这正是spring mvc的resolveException方法,若用上古处理方法,你需要去重写它。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

而在执行resolveException,会依次走更抽象的顶层方法,然后来到核心方法doResolveHandlerMethodException

在这里插入图片描述

doResolveHandlerMethodException方法中:他会干下面的事情

  • 1.根据HandlerMethod(要处理的方法)和exception获取异常处理的Method(先从exceptionHandlerCache查,再从advice中查)

  • 2.设置异常处理方法的参数解析器和返回值解析器(一般就是之前默认的argumentResolvers和returnValueHandlers)

  • 3.执行具体的异常处理方法(也就是1找到的Method)

  • 4.对返回的视图模型进行处理(这个不用太纠结,因为一般不返回视图了,并且一般在@ExceptionHandler标注的方法中我们会对请求处理的,所以一般会返回new ModelAndView(),表示不对视图进行进一步处理)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

重点就是如何根据HandlerMethod(要处理的方法)和exception获取异常处理的Method

先理解一下exceptionHandlerCache和exceptionHandlerAdviceCache。因为Method就从他们身上获取的。

4个map

exceptionHandlerCache,exceptionHandlerAdviceCache,mappedMethods,基于mappedMethods的缓存

直接看定义

	private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =new ConcurrentHashMap<>(64);private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =new LinkedHashMap<>();

exceptionHandlerCache存储@RequestMapping对应的ExceptionHandlerMethodResolver(就是本例中在@controller里的@ExceptionHandler)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

exceptionHandlerAdviceCache保存了@ControllerAdvice对应的ExceptionHandlerMethodResolver(在本例中就是global那个类)

还有mappedMethods,封装的异常对应的处理方法。和基于它的mappedMethods缓存

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为什么需要这个缓存后面说

回到如何根据HandlerMethod(要处理的方法)和exception获取异常处理的Method这个问题上来

直接看详细注释:

@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {Class<?> handlerType = null;if (handlerMethod != null) {//0.获取handlerMethod(deleteBook)的类(BookController)handlerType = handlerMethod.getBeanType();//1.从exceptionHandlerCache查(@Controller里的@ExpectionHandler)查一个resolverExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);//2.第一次一般为null,这时会根据handlerType去new一个resolver,若有handlerType对应的异常方法,这时这个resolver的mappedMethods有这个方法if (resolver == null) {resolver = new ExceptionHandlerMethodResolver(handlerType);this.exceptionHandlerCache.put(handlerType, resolver);}//3.根据异常解析这个方法,先从mappedMethods的缓存中查,查不出到mappedMethods查,然后写入缓存Method method = resolver.resolveMethod(exception);if (method != null) {//4.解析出方法了,在这个ServletInvocableHandlerMethod会回调具体方法return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);}// For advice applicability check below (involving base packages, assignable types// and annotation presence), use target class instead of interface-based proxy.if (Proxy.isProxyClass(handlerType)) {handlerType = AopUtils.getTargetClass(handlerMethod.getBean());}}//5.和上面类似,只是遍历所有的exceptionHandlerAdviceCache(@ControllerAdvice),从每一个entry里找出resolver,试图解析出exception对应的方法,然后调用for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {ControllerAdviceBean advice = entry.getKey();if (advice.isApplicableToBeanType(handlerType)) {ExceptionHandlerMethodResolver resolver = entry.getValue();Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);}}}return null;
}

经过测试,在本例中,

若存在三种异常处理方式

第一次执行:走234

第二次执行:走1(exceptionHandlerCache已被写入)

若把@Controller耦合@ExceptionHandler去掉,则走advice的cache(也就是global)。

若把global也去掉,则走最后那个上古版本的resolver,不会到这个getExceptionHandlerMethod方法的。直接执行resolveException了

所以优先级:@Controller耦合@ExceptionHandler > @ControllerAdvice+@ExceptionHandler > 实现handlerExceptionResolver+@Component(上古版本)

4个map的初始化

上面在探究如何根据HandlerMethod(要处理的方法)和exception获取异常处理的Method中,你会好奇这些map什么时候被初始化的

先说结论:

  • exceptionHandlerAdviceCache在ExceptionHandlerExceptionResolver被初始化的过程的afterPropertiesSet方法中赋值

  • exceptionHandlerCache则是在执行异常时,碰到了一场异常要处理了,再去初始化(也就是第二次执行中exceptionHandlerCache被写入)

handlerExceptionResolver作为bean注入容器:

spring mvc通过 WebMvcConfigurationSupport类(configuration类)+@Bean注解的方式来注入handlerExceptionResolver bean

并在上述的#addDefaultHandlerExceptionResolvers方法中,注册了3个处理器:分别处理@ExceptionHandler,@ResponseStatus标注的方法和默认异常解析器。

@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();//钩子方法,用于向列表中添加用户自定义的异常处理器。configureHandlerExceptionResolvers(exceptionResolvers);//向列表中添加Spring MVC的默认异常处理器if (exceptionResolvers.isEmpty()) {addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}//扩展或修改异常处理器列表extendHandlerExceptionResolvers(exceptionResolvers);//返回一个HandlerExceptionResolverComposite实例,它是handlerExceptionResolver一种实现HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);return composite;
}

在addDefaultHandlerExceptionResolvers

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,ContentNegotiationManager mvcContentNegotiationManager) {ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();//设置内容协商处理器(确定相应格式),消息转换器(java<->json),exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);exceptionHandlerResolver.setMessageConverters(getMessageConverters());exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());//若jackson存在,则给ResponseBodyAdvice设置一个JsonViewResponseBodyAdvice实例,用于处理jackson的jsonviewif (jackson2Present) {exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}if (this.applicationContext != null) {exceptionHandlerResolver.setApplicationContext(this.applicationContext);}//调用afterPropertiesSet,完成初始化exceptionHandlerResolver.afterPropertiesSet();exceptionResolvers.add(exceptionHandlerResolver);ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);exceptionResolvers.add(responseStatusResolver);exceptionResolvers.add(new DefaultHandlerExceptionResolver());//添加3个ExceptionResolver:分别处理@ExceptionHandler,@ResponseStatus,默认异常解析器
}

在exceptionHandlerResolver.afterPropertiesSet()中:

1.找到所有@ControllerAdvice注解的类,注册为bean

2.将每一个@ControllerAdvice注解的类与ExceptionHandlerMethodResolver的对应关系 写入exceptionHandlerAdviceCache中。

3.初始化argumentResolvers和returnValueHandlers(一般就是spring默认提供的各种processor和handler)

如returnValueHandlers的HttpEntityMethodProcessor,ModelAndViewMethodReturnValueHandler等等。

如argumentResolvers的**@SessionAttribute**,@RequestAttribute

private void initExceptionHandlerAdviceCache() {if (getApplicationContext() == null) {return;}//获取所有@ControllerAdvice的类List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();if (beanType == null) {throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);}ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);if (resolver.hasExceptionMappings()) {this.exceptionHandlerAdviceCache.put(adviceBean, resolver);}if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {this.responseBodyAdvice.add(adviceBean);}}if (logger.isDebugEnabled()) {int handlerSize = this.exceptionHandlerAdviceCache.size();int adviceSize = this.responseBodyAdvice.size();if (handlerSize == 0 && adviceSize == 0) {logger.debug("ControllerAdvice beans: none");}else {logger.debug("ControllerAdvice beans: " +handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");}}
}

//RequestMappingHandlerAdapter.java
@Override
public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}
}

https://www.cnblogs.com/java-chen-hao/p/11190659.html#_label0

https://blog.csdn.net/qq_26222859/article/details/51320493

https://blog.csdn.net/zzti_erlie/article/details/105746203

为什么需要基于mappedMethods缓存

因为一个异常可能有多个匹配方法,这时需要按照继承优先级对它进行排序,执行最近的那个。

由于最后只执行最近的那个method,那么我们只关心这个method就OK,所以将最近的这个method写入一个map,在每次查询时直接去查这个缓存并返回就好了,避免了排序

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

还是不会写源码分析,小小总结一下

总结一下

  • spring mvc有下面三种方式实现异常处理:

    • 实现handlerExceptionResolver+@Component(上古版本)

    • controller里耦合@ExceptionHandler(优先级最高)

    • @ControllerAdvice+@ExceptionHandler(最常用)

  • 若存在三种异常处理时,优先级为:@Controller耦合@ExceptionHandler > @ControllerAdvice+@ExceptionHandler > 实现handlerExceptionResolver+@Component(上古版本)(执行一次就ok)

    • 为什么:先查ExceptionHandler的cache,尝试解析一个方法,解析不出来再去查advice的cache。
  • spring mvc异常处理如何实现的

    • 当打开webMvcConfiguration后,会注入handlerExceptionResolver(一个bean),这个handlerExceptionResolver会注入@ExceptionHandler,@ResponseStatus,默认异常解析器3个处理器,最值得关注的是ExceptionHandler处理器。
    • 对于ExceptionHandler的处理器,会调用afterPropertiesSet方法完成initExceptionHandlerAdviceCache的注入
    • 当异常发生后,在执行resolveException会一层一层到doResolveHandlerMethodException方法,在这里寻找该异常匹配的方法,并回调。(完成类似aop切面的效果)。
  • 为什么mappedMethods需要缓存

    • 因为一个异常可能有多个匹配方法,这时需要按照继承优先级对它进行排序,执行最近的那个。
    • 由于最后只执行最近的那个method,所以将最近的这个method写入一个map,在每次查询时直接去查这个缓存并返回就好了,避免了排序
      onHandler处理器。
    • 对于ExceptionHandler的处理器,会调用afterPropertiesSet方法完成initExceptionHandlerAdviceCache的注入
    • 当异常发生后,在执行resolveException会一层一层到doResolveHandlerMethodException方法,在这里寻找该异常匹配的方法,并回调。(完成类似aop切面的效果)。
  • 为什么mappedMethods需要缓存

    • 因为一个异常可能有多个匹配方法,这时需要按照继承优先级对它进行排序,执行最近的那个。
    • 由于最后只执行最近的那个method,所以将最近的这个method写入一个map,在每次查询时直接去查这个缓存并返回就好了,避免了排序

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

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

相关文章

127.0.0.1与本机IP地址的区别

大家好&#xff0c;今天我们来聊聊一个在网络世界中常常被提及&#xff0c;但可能对于非专业人士来说还有些模糊的概念——127.0.0.1与本机IP地址。这两个地址在网络通信中都扮演着重要的角色&#xff0c;但它们之间又有着怎样的区别呢&#xff1f;让我们一起来探究一下。 一、…

简单Mesh多线程合并,使用什么库性能更高

1&#xff09;简单Mesh多线程合并&#xff0c;使用什么库性能更高 2&#xff09;Unity Semaphore.WaitForSignal耗时高 3&#xff09;VS编辑的C#代码注释的中文部分乱码 4&#xff09;变量IntPtr m_cachePtr切换线程后变空 这是第389篇UWA技术知识分享的推送&#xff0c;精选了…

【GO-OpenCV】go-cv快速配置

最近对golang实现目标检测心血来潮&#xff0c;尝试在没有sudo权限的平台配置go-cv,有所发现&#xff0c;索性多个平台都做尝试 安装Go语言&#xff08;Golang&#xff09; 通过包管理器安装&#xff08;适用于Debian/Ubuntu&#xff09;(有点慢) 更新包列表&#xff1a; sud…

AbMole带你探索颅内压力与肌肉生长的联系:一项突破性研究

在生物医学领域&#xff0c;颅内压力&#xff08;ICP&#xff09;的调控机制一直是研究的热点。最近&#xff0c;一项发表在《PLOS ONE》上的研究为我们揭示了颅内压力与后颅窝肌肉生长之间的潜在联系&#xff0c;为我们理解某些慢性头痛的成因提供了新的视角。 颅内压力的异常…

大数据的发展,带动电子商务产业链,促进了社会的进步【电商数据采集API接口推动电商项目的源动力】

最近几年计算机技术在诸多领域得到了有效的应用&#xff0c;同时在多方面深刻影响着我国经济水平的发展。除此之外&#xff0c;人民群众的日常生活水平也受大数据技术的影响。 在这其中电子商务领域也在大数据技术的支持下&#xff0c;得到了明显的进步。虽然电子商务领域的发…

酷开科技将AI与大数据融合,成为OTT大屏营销革新的驱动力

在数字化浪潮的推动下&#xff0c;营销领域正经历着深刻的变革。而在这样一个媒介渠道分散、注意力碎片化的时代&#xff0c;“大屏”是难得能让消费者们“精神集中”高度卷入的内容消费场景&#xff0c;也是能让品牌一对多地高效触达家庭人群的通道&#xff0c;大屏的独特营销…

TVBOX 最新版下载+视频源教程

下载链接 wx 搜索 Geek 前端 发送电视资源进行获取 操作教程

【数据结构与算法】对称矩阵,三角矩阵 详解

给出对称矩阵、三角矩阵的节省内存的存贮结构并写出相应的输入、输出算法。 对称矩阵和三角矩阵可以通过特殊的存储结构来节省内存。这种存储结构只存储矩阵的一部分元素&#xff0c;而不是全部元素。 对称矩阵&#xff1a;对于一个n阶对称矩阵&#xff0c;我们只需要存储主对…

VUE 项目用 Docker+Nginx进行打包部署

一、Docker Docker 是一个容器化平台&#xff0c;允许你将应用程序及其依赖项打包在容器中。使用 Docker&#xff0c;你可以创建一个包含 Vue.js 应用程序的容器镜像&#xff0c;并在任何支持 Docker 的环境中运行该镜像。 二、Nginx Nginx 是一个高性能的 HTTP 服务器和反向…

钡铼技术BL104在环境监测站多协议采集保障数据全面准确

随着工业化和城市化进程的加快&#xff0c;环境污染问题日益严重&#xff0c;环境监测站在保护生态环境、保障公众健康中的作用变得越来越重要。钡铼技术PLC物联网关BL104&#xff0c;为环境监测站提供了一种高效、可靠的多协议数据采集解决方案&#xff0c;保障了监测数据的全…

Hype 4(html5工具) mac版下载-Hype 4 for mac软件最新版下载附加详细安装步骤

用户量向我们证明了矢量形状&#xff0c;矢量是使用矢量工具绘制的形状&#xff0c;包括直线&#xff0c;曲线和复杂形状。有目共睹的是Hype是一款强大的Mac OS平台 HTML5 创作工具&#xff0c;它能够在网页上做出赏心悦目的动画片效果&#xff0c;创建丰富的网页交互动画片&am…

域策略笔记

域策略 导航 文章目录 域策略导航一、设置客户端壁纸二、重定向用户配置文件路径三、部署网络打印机四、部署共享文件夹为网络驱动器五、通过域策略推送软件安装六、通过域策略限制软件的使用通过路径进行限制通过进程限制 七、通过域策略将文件添加白名单八、通过域策略添加可…

大数据集群离线解析经纬度逆编码地址

背景 最近有个需要需求把经纬度解析为地址&#xff0c;那么通常解析地址市面上流行的方案就是调取百度、高德地图的接口进行解析。 难点 但是在用这个方案遇到一个问题就是企业认证的百度地图每天的逆编码解析为300w次&#xff0c;qps为100次/秒&#xff0c;对于日增上千万的…

大数据实训项目(小麦种子)-03、大数据环境Hadoop、Mapreduce、Hive、Hbase、HDFS搭建服务及调试

文章目录 前言一、Linux系统Centos7安装配置JDK8二、Linxu系统Centos7中搭建Hadoop3.1.0服务下载地址服务1&#xff1a;详细步骤&#xff08;初始化与启动dfs服务&#xff09;详细步骤配置环境变量 服务2&#xff1a;Hadoop(YARN)环境搭建 三、Linux系统搭建Hive3.1.2服务前提条…

SysTools MailXaminer: 电子邮件取证调查中的链接分析和时间线分析

天津鸿萌科贸发展有限公司是 SysTools 系列软件的授权代理商。 SysTools MailXaminer 电子邮件取证软件提供全面强大的解决方案&#xff0c;通过简化的操作&#xff0c;从电子邮件客户端、网络邮箱服务器、磁盘镜像、Skype 通讯工具中解密并搜索证据。软件对调查工作的每一阶段…

基于 Transformer 的大语言模型

语言建模作为语言模型&#xff08;LMs&#xff09;的基本功能&#xff0c;涉及对单词序列的建模以及预测后续单词的分布。 近年来&#xff0c;研究人员发现&#xff0c;扩大语言模型的规模不仅增强了它们的语言建模能力&#xff0c;而且还产生了处理传统NLP任务之外更复杂任务…

这些已经死去的软件,依旧无可替代

互联网这条长河里&#xff0c;软件们就像流星一样&#xff0c;一闪而过。有的软件火过一段时间&#xff0c;然后就慢慢消失了。 说不定有些软件你以前天天用&#xff0c;但不知道从什么时候开始就不再用了。时间一天天过去&#xff0c;我们的热情、记忆都在消退&#xff0c;还…

七、(正点原子)Linux并发与竞争

Linux是多任务操作系统&#xff0c;肯定会存在多个任务共同操作同一段内存或者设备的情况&#xff0c;多个任务甚至中断都能访问的资源叫做共享资源。在驱动开发中要注意对共享资源的保护&#xff0c;也就是要处理对共享资源的并发访问。 一、并发与竞争 1、简介 并发就是多个…

计算机行业的现状与未来之2024

年年都说编程好&#xff0c;编程工资涨不了。 人家骑车送外卖&#xff0c;月入两万好不好。 一、计算机专业的背景与现状 在过去几十年里&#xff0c;计算机科学相关专业一直是高考考生的热门选择。无论是计算机科学与技术、软件工程&#xff0c;还是人工智能与大数据&#xff…

PR软件视频抠图换背景

1 新建项目 2 新建序列 在项目的右下角有个图标&#xff0c;新建 序列 序列是视频的制作尺寸&#xff0c;根据自己的需要选择 3 新建颜色遮罩 在项目的右下角--新建颜色遮罩--选择黑色--确定 4 导入视频 把要导入视频的文件夹打开&#xff0c;把视频拖到 项目 里 把黑色遮罩拖…