目录

  • 前言

  • Spring Boot 版本

  • 全局统一异常处理的前世今生

  • Spring Boot的异常如何分类?

  • 如何统一异常处理?

  • 异常匹配的顺序是什么?

  • 总结

前言

软件开发过程中难免遇到各种的BUG,各种的异常,一直就是在解决异常的路上永不停歇,如果你的代码中再出现try(){...}catch(){...}finally{...}代码块,你还有心情看下去吗?自己不觉得恶心吗?

冗余的代码往往回丧失写代码的动力,每天搬砖似的写代码,真的很难受。今天这篇文章教你如何去掉满屏的try(){...}catch(){...}finally{...},解放你的双手。

Spring Boot 版本

本文基于的Spring Boot的版本是2.3.4.RELEASE

全局统一异常处理的前世今生

早在Spring 3.x就已经提出了@ControllerAdvice,可以与@ExceptionHandler@InitBinder@ModelAttribute 等注解注解配套使用,这几个此处就不再详细解释了。

这几个注解小眼一瞟只有@ExceptionHandler与异常有关啊,翻译过来就是异常处理器其实异常的处理可以分为两类,分别是局部异常处理全局异常处理

局部异常处理@ExceptionHandler@Controller注解搭配使用,只有指定的controller层出现了异常才会被@ExceptionHandler捕获到,实际生产中怕是有成百上千个controller了吧,显然这种方式不合适。

全局异常处理:既然局部异常处理不合适了,自然有人站出来解决问题了,于是就有了@ControllerAdvice这个注解的横空出世了,@ControllerAdvice搭配@ExceptionHandler彻底解决了全局统一异常处理。当然后面还出现了@RestControllerAdvice这个注解,其实就是@ControllerAdvice@ResponseBody结晶。

Spring Boot的异常如何分类?

Java中的异常就很多,更别说Spring Boot中的异常了,这里不再根据传统意义上Java的异常进行分类了,而是按照controller进行分类,分为进入controller前的异常业务层的异常,如下图:

进入controller之前异常一般是javax.servlet.ServletException类型的异常,因此在全局异常处理的时候需要统一处理。几个常见的异常如下:

  1. NoHandlerFoundException:客户端的请求没有找到对应的controller,将会抛出404异常。

  2. HttpRequestMethodNotSupportedException:若匹配到了(匹配结果是一个列表,不同的是http方法不同,如:Get、Post等),则尝试将请求的http方法与列表的控制器做匹配,若没有对应http方法的控制器,则抛该异常

  3. HttpMediaTypeNotSupportedException:然后再对请求头与控制器支持的做比较,比如content-type请求头,若控制器的参数签名包含注解@RequestBody,但是请求的content-type请求头的值没有包含application/json,那么会抛该异常(当然,不止这种情况会抛这个异常)

  4. MissingPathVariableException:未检测到路径参数。比如url为:/user/{userId},参数签名包含@PathVariable("userId"),当请求的url为/user,在没有明确定义url为/user的情况下,会被判定为:缺少路径参数

如何统一异常处理?

在统一异常处理之前其实还有许多东西需要优化的,比如统一结果返回的形式。当然这里不再细说了,不属于本文范畴。

统一异常处理很简单,这里以前后端分离的项目为例,步骤如下

  1. 新建一个统一异常处理的一个类

  2. 类上标注@RestControllerAdvice这一个注解,或者同时标注@ControllerAdvice@ResponseBody这两个注解。

  3. 在方法上标注@ExceptionHandler注解,并且指定需要捕获的异常,可以同时捕获多个。

下面是作者随便配置一个demo,如下:

/*** 全局统一的异常处理,简单的配置下,根据自己的业务要求详细配置*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {/*** 重复请求的异常* @param ex* @return*/@ExceptionHandler(RepeatSubmitException.class)public ResultResponse onException(RepeatSubmitException ex){//打印日志log.error(ex.getMessage());//todo 日志入库等等操作//统一结果返回return new ResultResponse(ResultCodeEnum.CODE_NOT_REPEAT_SUBMIT);}/*** 自定义的业务上的异常*/@ExceptionHandler(ServiceException.class)public ResultResponse onException(ServiceException ex){//打印日志log.error(ex.getMessage());//todo 日志入库等等操作//统一结果返回return new ResultResponse(ResultCodeEnum.CODE_SERVICE_FAIL);}/*** 捕获一些进入controller之前的异常,有些4xx的状态码统一设置为200* @param ex* @return*/@ExceptionHandler({HttpRequestMethodNotSupportedException.class,HttpMediaTypeNotSupportedException.class, HttpMediaTypeNotAcceptableException.class,MissingPathVariableException.class, MissingServletRequestParameterException.class,ServletRequestBindingException.class, ConversionNotSupportedException.class,TypeMismatchException.class, HttpMessageNotReadableException.class,HttpMessageNotWritableException.class,MissingServletRequestPartException.class, BindException.class,NoHandlerFoundException.class, AsyncRequestTimeoutException.class})public ResultResponse onException(Exception ex){//打印日志log.error(ex.getMessage());//todo 日志入库等等操作//统一结果返回return new ResultResponse(ResultCodeEnum.CODE_FAIL);}
}

注意上面的只是一个例子,实际开发中还有许多的异常需要捕获,比如TOKEN失效过期等等异常,如果整合了其他的框架,还要注意这些框架抛出的异常,比如ShiroSpring Security等等框架。

异常匹配的顺序是什么?

有些朋友可能疑惑了,如果我同时捕获了父类和子类,那么到底能够被那个异常处理器捕获呢?比如ExceptionServiceException

此时可能就疑惑了,这里先揭晓一下答案,当然是ServiceException的异常处理器捕获了,精确匹配,如果没有ServiceException的异常处理器才会轮到它的父亲父亲没有才会到祖父。总之一句话,精准匹配,找那个关系最近的。

为什么呢?这可不是凭空瞎说的,源码为证,出处org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#getMappedMethod,如下:

@Nullableprivate Method getMappedMethod(Class<? extends Throwable> exceptionType) {List<Class<? extends Throwable>> matches = new ArrayList<>();//遍历异常处理器中定义的异常类型for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {//是否是抛出异常的父类,如果是添加到集合中if (mappedException.isAssignableFrom(exceptionType)) {    //添加到集合中matches.add(mappedException);  }}//如果集合不为空,则按照规则进行排序if (!matches.isEmpty()) {matches.sort(new ExceptionDepthComparator(exceptionType));//取第一个return this.mappedMethods.get(matches.get(0));}else {return null;}}

在初次异常处理的时候会执行上述的代码找到最匹配的那个异常处理器方法,后续都是直接从缓存中(一个Map结构,key是异常类型,value是异常处理器方法)。

别着急,上面代码最精华的地方就是对matches进行排序的代码了,我们来看看ExceptionDepthComparator这个比较器的关键代码,如下:

//递归调用,获取深度,depth值越小越精准匹配
private int getDepth(Class<?> declaredException, Class<?> exceptionToMatch, int depth) {//如果匹配了,返回if (exceptionToMatch.equals(declaredException)) {// Found it!return depth;}// 递归结束的条件,最大限度了if (exceptionToMatch == Throwable.class) {return Integer.MAX_VALUE;}//继续匹配父类return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1);}

精髓全在这里了,一个递归搞定,计算深度,depth初始值为0。值越小,匹配度越高越精准。

总结

全局异常的文章万万千,能够讲清楚的能有几篇呢?只出最精的文章,做最野的程序员,如果觉得不错的,关注分享走一波,谢谢支持!!!

满屏的try-catch,你不瘆得慌?相关推荐

  1. 不要再满屏写 try...catch 了!这个更香!

    点击关注公众号,Java干货及时送达 来源:www.toutiao.com/i6878184496945070604 前言 软件开发springboot项目过程中,不可避免的需要处理各种异常,spri ...

  2. 求求你们了,别再写满屏的 try catch 了!!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:巨人大哥 来源:cnblogs.com/jurendage/ ...

  3. java springboot使用itext 为PDF添加文字水印并铺满屏

    java springboot使用itextpdf 为PDF添加文字水印并铺满屏 代码如下 如果对你有帮助希望你能点个赞 结尾 这是我第一次发博客,也是第一次使用itext添加水印.遇到一些大大小小的 ...

  4. 图片加水印后下载(满屏水印,单个水印,可自定义角度)

    现阶段需要实现图片导出添加水印的内容,查询资料总结了如下的图片导出加水印工具类,分享给大家 工具类实现内容: 图片添加满屏水印后保存到固定地址 图片添加满屏水印后以输出流输出 图片左上角添加单个水印 ...

  5. qq满屏飞吻代码_教你用微信隐藏代码表白!各种微信技巧

    阅读本文前,请您先点击上面的"蓝色字体",再点击"关注",这样您就可以继续免费收到文章了.每天都会有分享,都是免费订阅,请您放心关注.注:本文转载自网络,不代表 ...

  6. 一款jQuery满屏自适应焦点图切换特效

    一款jQuery满屏自适应焦点图切换特效 ,自适应当前浏览器的宽度,可以作为网站整个大背景的却换效果,很不错的一款不jquery特效. 兼容性没的说直接秒杀了IE6.适用浏览器:IE6.IE7.IE8 ...

  7. 打开了悬浮窗权限但是没有_给你的手机添加“樱花雨”特效,打开手机樱花就满屏飘落,漂亮!...

    记得用塞班系统的手机时有很多手机特效补丁,随着科技的改朝换代现在的安卓手机貌似没有那些特效补丁了,回想曾经的诺基亚时代很是回味无穷,还好今天小编就带大家重回那些金典的画面,屏幕下雨,满屏樱花飞舞,等等 ...

  8. PyCharm去掉满屏的波浪线

    首先打开: File>Settings>Editor>Colors&Fonts>General 选中1去掉2的勾选,就可以解决满屏的波浪线,其它雷同. 转载于:http ...

  9. 400+的考研复试线怎么玩???满屏都是400+?今年国家线会涨吗?

    关注.星标公众号,直达精彩内容 有不少同学在微博晒出分数,不少同学分数400+,今天大部分地区出分了,惊现477分的大神:政治91分,英语二95分,数学三150分,专业课141分.如果分数是真实的,这 ...

最新文章

  1. Flask之Cookie与Session
  2. hibernate注解之@Onetomany、@Manytoone、@JoinColumn
  3. 内存泄漏检测工具(转载)
  4. 常见Java面试题 – 第二部分:equals与==
  5. 手把手教你java快速过滤关键词
  6. C语言去括号编程题,去括号 - C语言网
  7. 爬取词库,使用jieba分词库,自定义dict.txt文件+将搜狗词库.scel文件为.txt文件
  8. 列表、字典补充点、strJoin方法、set()集合、和深浅拷贝
  9. flink sql设置并行度_Flink集成Hivestream模式用例
  10. 常见的Activity Action Intent常量
  11. C语言const的用法
  12. exe电子书转换txt 下载_如何把电子书转换成适合Kindle 的格式?这是最全的攻略...
  13. geexbox 编译
  14. 优酷路由宝刷梅林_【荒野无灯Padavan固件】优酷路由宝L1内存卡扩展SWAP缓存+v2瑞设置详解...
  15. javaUDP逐步实现多线程发送和接收消息
  16. win10电脑亮度调节失灵(win10电脑亮度调节失灵戴尔)
  17. RFC1952的部分翻译及原文 (转)
  18. 计算机毕业设计Java毕业论文答辩管理系统(源码+系统+mysql数据库+lw文档)
  19. 视频教程-基于Java的WebSocket的聊天室-Java
  20. 基于EP4CE10F17C8N芯片详解Altera Cyclone系列器件命名规则

热门文章

  1. 幂等性 第三方交易编号_java幂等性的解决方案
  2. LibreOffice 支持无障碍辅助的 5 种方式
  3. 用Python批量实现多Excel多Sheet合并的4种方法
  4. 跳过51单片机,直接学STM32有什么严重后果?
  5. 关于学习Python的一点学习总结(31->继承及多态)
  6. 根号分治 ---- D. Mr. Kitayuta‘s Colorful Graph(根号均摊复杂度 + 数据结构维护)
  7. html边框绕着图片,CSS 边框
  8. kuangbin带你飞专题合集
  9. UVA839 天平 Not so Mobile(二叉树的递归遍历建树并回答问题)
  10. java多线程机制_Java的多线程机制