Spring MVC 十:异常处理

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

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

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

    @GetMapping(value="/hello",produces={"text/html; charset=UTF-8"})@ResponseBodypublic 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"})@ResponseBodypublic String hello(ModelAndView model){int c = 100 / 0;return "<h1>User info</h1>";}@ExceptionHandler(Exception.class)@ResponseBodypublic 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干的,源码中可以看到:

@Nullableprotected 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)@ResponseBodypublic String handle(Exception ex){return return "错了MyGlobalExceptionController error msg is ===";}
}

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

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

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

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

自定义异常处理器HandlerExceptionResolver

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

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

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

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

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

相关文章

【数据结构与算法】- 数组

数组 1.1 数组的定义1.2 数组的创建1.3 数组在内存中的情况2.1 初始化数组2.2 插入元素2.3 删除元素2.4 读取元素2.5 遍历数组 1.1 数组的定义 数组中的是在内存中是连续存储的&#xff0c;内存是由一个个内存单元组成的&#xff0c;每一个内存单元都有自己的地址&#xff0c;…

优先级队列

优先级队列 一端进&#xff0c;另一端出 按优先级出队 普通队列 一端进&#xff0c;另一端出 FIFO 我们做个约定&#xff1a;数字大的&#xff0c;优先级越高 &#xff0c;优先出队 无序数组实现 要点 入队保持顺序 出队前找到优先级最高的出队&#xff0c;相当于一次选择排序…

Tensorflow2 GPU 安装方法

一、Tensorflow2 GPU 安装方法 1. 首先安装Anaconda3环境2. 在Anaconda Prompt 中安装tensorflow23. 验证GPU是否可以使用4. 错误解决 1. 首先安装Anaconda3环境 https://www.anaconda.com/ 2. 在Anaconda Prompt 中安装tensorflow2 conda update conda conda create -n ten…

Unity把UGUI再World模式下显示到相机最前方

Unity把UGUI再World模式下显示到相机最前方 通过脚本修改Shader 再VR里有时候要把3D的UI显示到相机最前方&#xff0c;加个UI相机会坏事&#xff0c;可以通过修改unity_GUIZTestMode来解决。 测试用例 测试用例如下&#xff1a; 场景包含一个红色的盒子&#xff0c;一个UI…

github搜索技巧

指定语言 language:java 比如我要找用java写的含有blog的内容 搜索项目名称包含关键词的内容 vue in:name 其他如项目描述跟项目文档&#xff0c;如下 组合使用 vue in:name,description,readme 根据Star 或者fork的数量来查找 总结 springboot vue stars:>1000 p…

关于字符拼接

当然&#xff0c;以下是加入了幽默注释的代码和对应的逻辑树&#xff1a; # 提示用户输入input和txt内容&#xff0c;期待用户真有输入 input_text input("请输入input文本&#xff1a;") # 好了&#xff0c;快点输入吧 txt_text input("请输入txt文本&#…

【数据结构】红黑树(C++实现)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【数据…

Android逆向学习(五)app进行动态调试

Android逆向学习&#xff08;五&#xff09;app进行动态调试 一、写在前面 非常抱歉鸽了那么久&#xff0c;前一段时间一直在忙&#xff0c;现在终于结束了&#xff0c;可以继续更新android逆向系列的&#xff0c;这个系列我会尽力做下去&#xff0c;然后如果可以的话我看看能…

华为云HECS云服务器docker环境下安装nginx

前提&#xff1a;有一台华为云服务器。 华为云HECS云服务器&#xff0c;安装docker环境&#xff0c;查看如下文章。 华为云HECS安装docker-CSDN博客 一、拉取镜像 下载最新版Nginx镜像 (其实此命令就等同于 : docker pull nginx:latest ) docker pull nginx查看镜像 dock…

SpringBoot中使用Servlet和Filter

为什么要把Servlet和Filter写在一起,因为使用方式很相似 两种方式 第一种,使用Servlet和Filter 使用Servlet 继承HttpServlet 注册Servlet 使用Filter 1.自定义过滤器 2.注册过滤器 这里注意一点 使用/**无效 至少我这2.4.5版本是这样 过滤所有请求用/* 那么其实还有…

(unordered)map和set封装(底层红黑树)

map和set封装 文章目录 map和set封装设计问题&#xff08;知其所以然&#xff09;为什么要对iterator进行封装&#xff1f;为什么要引入Self Ref Ptr这些模板参数&#xff1f;为什么是试图从non_const转变为const&#xff0c;而不是const转为non_const如何解决 为什么说能加con…

【C++】模板初阶 -- 详解

一、泛型编程 // 实现一个通用的交换函数&#xff1a; void Swap(int& left, int& right) {int temp left;left right;right temp; }void Swap(double& left, double& right) {double temp left;left right;right temp; }void Swap(char& left, ch…