Spring MVC 十:异常处理

news2025/1/21 11:23:33

异常是每一个应用必须要处理的问题。

Spring MVC项目,如果不做任何的异常处理的话,发生异常后,异常堆栈信息会直接抛出到页面。

比如,我们在Controller写一个异常:

    @GetMapping(value="/hello",produces={"text/html; charset=UTF-8"})
    @ResponseBody
    public String hello(ModelAndView model){
        int c = 100 / 0;
        return "<h1>User info</h1>";
    }

浏览器访问:
在这里插入图片描述

用户体验相当不好,所以一般情况下,应用必须要想办法处理异常。

异常处理方式

常见的异常处理方式,无非:

  1. 应用中对所有可能发生异常的地方,都try catch,捕获异常后做相应的处理。
  2. 集中处理异常。

第一种方式显然不好,一方面是代码中需要到处都写try catch,万一某一段代码由于程序员的疏忽没有写,异常就会抛出到前台,很不好。

另外,某些情况下异常是不能捕获的,比如需要事务处理的代码,捕获异常后会影响到事务回滚。

所以,我们还是需要想办法用第2中方式来处理异常。n多年前曾经做过一个摩托罗拉的项目,其中一项需求就是对一个线上系统的异常做处理、不允许异常信息抛出到前台页面。当初那个项目并没有采用类似SpringMVC的框架,所以最终提出了用filter、在filter中拦截异常的处理方案并且被客户采纳。

其实SpringMVC的统一异常处理方案和我上面项目中采用的方案非常类似。

SpringMVC的异常处理

Spring MVC提供了如下异常处理选项:

  1. @ExceptionHandle
  2. @ExceptionHandle + @ControllerAdvice
  3. 自定义异常处理器HandlerExceptionResolver
@ExceptionHandle

@ExceptionHandle可以作用在Controller中,比如,在上面发生异常的Controller中增加一个@ExceptionHandle注解的方法:

    @GetMapping(value="/hello",produces={"text/html; charset=UTF-8"})
    @ResponseBody
    public String hello(ModelAndView model){
        int c = 100 / 0;
        return "<h1>User info</h1>";
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public String handle(Exception ex){
        return "错了在helloworld Controller error msg is ===";
    }

运行:
在这里插入图片描述

说明Hello方法中发生的异常,已经被handle方法处理,前台页面不再会出现异常信息。

问题解决了!

但是@ExceptionHandle只在当前Controller文件中生效,也就是说,当前Controller中的方法、或者方法调用的service层、dao层等发生的异常,才会被捕获到。其他Controller中发生的异常依然不会被捕获。

这样的话,就需要对每一个Controller增加@ExceptionHandle进行处理,处理起来还是有点麻烦。

统一异常处理

@ExceptionHandle + @ControllerAdvice可以实现统一异常处理。

@ControllerAdvice注解可以实现:

On startup, RequestMappingHandlerMapping and ExceptionHandlerExceptionResolver detect controller advice beans and apply them at runtime. Global @ExceptionHandler methods, from an @ControllerAdvice, are applied after local ones, from the @Controller. By contrast, global @ModelAttribute and @InitBinder methods are applied before local ones.

SpringMVC启动的过程中,RequestMappingHandlerMapping和ExceptionHandlerExceptionResolver会检测到advice bean并且在运行时会使用他们。在@ControllerAdvice中的@ExceptionHandler会变成一个全局的异常处理器、在本地异常处理器之后生效。并且,@ModelAttribute和 @InitBinder会在本地的之后生效。

这段话的意思就是,@ExceptionHandle + @ControllerAdvice之后,@ExceptionHandle就会变成全局异常处理器。所谓的本地异常处理器,就是写在Controller中的@ExceptionHandle异常处理器。

这个工作是ExceptionHandlerExceptionResolver干的,源码中可以看到:

@Nullable
	protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
			@Nullable HandlerMethod handlerMethod, Exception exception) {

		Class<?> handlerType = null;
        //先找"local"异常处理器
		if (handlerMethod != null) {
			// Local exception handler methods on the controller class itself.
			// To be invoked through the proxy, even in case of an interface-based proxy.
			handlerType = handlerMethod.getBeanType();
			ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
			if (resolver == null) {
				resolver = new ExceptionHandlerMethodResolver(handlerType);
				this.exceptionHandlerCache.put(handlerType, resolver);
			}
			Method method = resolver.resolveMethod(exception);
			if (method != null) {
				return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
			}
			// 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());
			}
		}
        //再找advice的异常处理器
		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);
				}
			}
		}

		return null;
	}

验证一下。

加一个MyGlobalExceptionController:

@ControllerAdvice
public class MyGlobalExceptionController {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public String handle(Exception ex){
        return return "错了MyGlobalExceptionController error msg is ===";
    }
}

先不去掉HelloWorldController中的异常处理器,运行hello:
在这里插入图片描述

生效的是HelloWorldController中的异常处理器。

然后去掉HelloWorldController中的异常处理器:
在这里插入图片描述

写在MyGlobalExceptionController中的全局异常处理器生效!

自定义异常处理器HandlerExceptionResolver

实现接口HandlerExceptionResolver,或者扩展虚拟类AbstractHandlerMethodExceptionResolver(勉强可以考虑),定义自己的异常处理器。

其实,非必要(没想到有什么必要性)不建议!

上一篇 Spring MVC 九:Context层级(基于配置)

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

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

相关文章

200行C++代码写一个Qt俄罗斯方块小游戏

小小演示一下&#xff1a; 大体思路&#xff1a; 其实很早就想写一个俄罗斯方块了&#xff0c;但是一想到那么多方块还要变形&#xff0c;还要判断落地什么的就脑壳疼。直到现在才写出来。 俄罗斯方块这个小游戏的小难点其实就一个&#xff0c;就是方块的变形&#xff0c;看似…

【智能家居项目】裸机版本——项目介绍 | 输入子系统(按键) | 单元测试

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f3c0;项目简介&#x1f3c0;输入子系统(按键)⚽应用层⚽设备层⚽ 内核层抽象层⚽…

基于谷歌Transeformer构建人工智能问答系统

目录 1 项目背景 2 关键技术 2.1 Transeformer模型 2.2 Milvus向量数据库 3 系统代码实现 3.1 运行环境构建 3.2 数据集介绍 3.3 预训练模型下载 3.4 代码实现 3.4.1 创建向量表和索引 3.4.2 构建向量编码模型 3.4.3 数据向量化与加载 3.4.4 构建检索web 3.5 运行结…

VS+Qt+C++ GDAL读取tif图像数据显示

程序示例精选 VSQtC GDAL读取tif图像数据显示 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《VSQtC GDAL读取tif图像数据显示》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;…

基于SpringBoot的美容院管理系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 美容部位管理 销量信息统计 已支付订单 技师功能实现 统计美容用品库存 预约信息管理 前台功能实现 普通用户管理 会员管理 普通用户功能实现 美容用品 购物车 我的订单 会员功能实现 美容项目 预约信…

PyCharm中使用pyqt5的方法2-1

qt可以用来设计界面&#xff0c;而pyqt是将qt移植到Python上&#xff0c;通过python语言设计界面&#xff0c;目前最新的版本是qt5。 在PyCharm中使用pyqt5的步骤分为下载和配置两个部分。 1 在PyCharm中下载安装pyqt5相关模块 1.1 下载步骤 PyCharm中要下载的pyqt5相关模块…

kafka集群是如何选择leader,你知道吗?

前言 kafka集群是由多个broker节点组成&#xff0c;这里面包含了许多的知识点&#xff0c;以下的这些问题你都知道吗? 你知道topic的分区leader是怎么选举的吗&#xff1f;你知道zookeeper中存储了kafka的什么信息吗&#xff1f;起到什么做呢&#xff1f;你知道kafka消息文件…

【精彩回顾】 用sCrypt在Bitcoin上构建智能合约

2023年3月24日&#xff0c;sCrypt在英国Exeter大学举办了关于智能合约的大学讲学。sCrypt首席执行官刘晓晖做了题为“用sCrypt在Bitcoin上构建智能合约”的演讲&#xff0c;并与到场的老师、学生进行了深入交流、互动。这次课程着重讲解了 BSV 智能合约的基础概念&#xff0c;以…

一图读懂「五度易链」企业创新服务解决方案,打造卓越营商环境!

“五度易链”紧密围绕园区企业及产业发展需求&#xff0c;基于数据积累和应用&#xff0c;创新企业服务机制&#xff0c;提升企业服务效能&#xff0c;以数字化手段为企业发展纾困解难&#xff0c;赋能企业高质量发展。并帮助园区在运营方面打破数据壁垒&#xff0c;实现数据监…

调度算法+等待/周转时间计算

周转时间 作业完成时刻 - 到达时刻 等待时间 开始时刻 - 到达时刻 平均时间就是用总时间除以作业个数 先来先服务调度算法&#xff08;FCFS&#xff09; 非抢占 优先级调度算法 系统总是调度优先级最高的那个进程运行。 优先级可以分为静态优先级和动态优先级。静态优先…

113. 路径总和ii

力扣题目链接(opens new window) 给定一个二叉树和一个目标和&#xff0c;找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树&#xff0c;以及目标和 sum 22&#xff0c; 在路径总和题目的基础上&…

新媒体运营的未来:ChatGPT的智能助手

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 新媒体运营是数字时代的…

Java括号匹配

目录 一、题目描述 二、题解 一、题目描述 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭…

LwIP笔记01:LwIP入门

1. LwIP简介 小型开源的TCP/IP协议栈交换机、路由器、光纤收发器、云台接入、无线网关、远程模块、工业控制器、网络摄像头 TCP/IP模型 &#xff08;1&#xff09;应用层&#xff1a;HTTP、MQTT、NTP、FTP、...... &#xff08;2&#xff09;传输层&#xff1a;TCP、UDP &…

【kubernetes】【基础资源使用】kubernetes中的Deployment使用

1 Why need Deployment? K8S中Pod是用户管理工作负载的基本单位&#xff0c;Pod通常通过Service进行暴露&#xff0c;因此&#xff0c;通常需要管理一组Pod&#xff0c;RC和RS主要就实现了一组Pod的管理工作&#xff0c;其中&#xff0c;RC和RS的区别在于&#xff0c;RS提供更…

如何使用pycharm连接Mysql数据库!!!

1、Mysql的安装&#xff1a; MySQL针对不同的用户提供了2中不同的版本&#xff1a; MySQL Community Server&#xff1a;社区版。由MySQL开源社区开发者和爱好者提供技术支持&#xff0c;对开发者开放源代码并提供免费下载。MySQL Enterprise Server&#xff1a;企业版。包括最…

新手程序员怎么接单?

程序员如何在自己年富力强的时候&#xff0c;最大化发挥自己的能力&#xff1f;将超能力转化为“钞能力”&#xff1f; 有人还在苦哈哈当老黄牛&#xff0c;一身使不完的牛劲&#xff0c;有人已经另辟蹊径&#xff0c;开创了自己的一片致富小天地。 接单找兼职&#xff0c;就…

MyBatis-Plus多数据源——如何在一个项目中使用多个MySQL数据库

前言 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window) 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 本系列博客结合实际应用场景&#xff0c;阐述MyBatis-Plus实际…

小程序中如何同步公众号的文章

小程序为了更好的服务客户&#xff0c;有时候需要显示公众号的文章&#xff0c;用于更具体介绍产品、关注公众号和会员服务等。下面就将具体介绍&#xff1a;小程序中如何同步显示公众号的文章。 1. 关联公众号。在管理员后台->会员管理->通知处&#xff0c;关联服务号。…

【大数据开发技术】实验05-HDFS目录与文件的创建删除与查询操作

文章目录 HDFS目录与文件的创建删除与查询操作一、实验目标二、实验要求三、实验内容四、实验步骤附&#xff1a;系列文章 HDFS目录与文件的创建删除与查询操作 一、实验目标 熟练掌握hadoop操作指令及HDFS命令行接口掌握HDFS目录与文件的创建方法和文件写入到HDFS文件的方法…