当前位置:首页 > 公众号精选 > 架构师社区
[导读]目录前言SpringBoot版本全局统一异常处理的前世今生SpringBoot的异常如何分类?如何统一异常处理?异常匹配的顺序是什么?总结前言软件开发过程中难免遇到各种的BUG,各种的异常,一直就是在解决异常的路上永不停歇,如果你的代码中再出现try(){...}catch(){...

目录

  • 前言
  • 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,如下:

@Nullable
 private Method getMappedMethod(Class exceptionType) {
  List> matches = new ArrayList<>();
    //遍历异常处理器中定义的异常类型
  for (Class 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。值越小,匹配度越高越精准。

总结

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

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

央视《今日说法》栏目近期报道了一名90后程序员通过开发非法视频搬运软件在不到一年的时间里获利超700万,最终获刑的案例。

关键字: 程序员 软件

业内消息,近日美国麦肯锡公司的一份报告强调了芯片行业的劳动力挑战,在美国寻求吸引更多技术工人从事半导体制造之际,许多现有员工正在重新考虑是否要留下来。

关键字: 芯片

业内消息,近日美国商务部工业与安全局(BIS)根据出口管理条例(EAR)将中国电子科技集团旗下多个研究所、中电科芯片技术(集团)有限公司、中国科学技术大学,以及北京量子信息科学研究院、本源量子等 37 个中国实体添加到实...

关键字: 实体清单

5 月 11 日消息,鲲鹏昇腾开发者大会昨日在北京中关村国际创新中心举办,主题为“心怀挚爱,共绽光芒”,会上推出了原生使能计划、启动鲲鹏昇腾科教创新卓越中心、鲲鹏昇腾原生创新汇等。

关键字: 华为 鲲鹏昇腾

近日路透社援引知情人士的消息称,美国商务部正考虑推动一项新的监管措施,将限制 AI 模型的出口,初步计划对 ChatGPT 等大模型采取行动。

关键字: AI

近日,特斯拉发布了Optimus最新进展视频,展现了其分拣电池、行走、执行工厂任务的能力,并配文“最近正在努力变得有用!”。

关键字: 特斯拉 机器人 Optimus

2024年5月9日晚,中国大陆晶圆代工龙头厂中芯国际发布2024年第一季度财报,销售收入为17.5亿美元,环比增长4.3%,同比增长19.7%;毛利率为13.7%,均好于指引。值得一提的是,这也是中芯国际的季度营收首次超...

关键字: 中芯国际

据韩联社报道,近日 SK 海力士子公司 SK 海力士系统集成电路拟以3.493亿美元的价格向无锡产业发展集团有限公司转让所持有的 SK 海力士系统集成电路(无锡)有限公司(下文简称无锡晶圆厂) 49.9% 股权。

关键字: SK 海力士 晶圆厂

近日,美国空军在加州爱德华兹空军基地进行了首次正式的AI控制战斗机试飞,美联社等少数媒体代表受邀观摩,美国空军方面明确表示,AI战机将是未来美国空中力量的重要组成部分。

关键字: 美国 AI

从近期媒体的一份爆料来看,苹果近年来其实已经下了不少力气深耕AI领域——在过去六年间从谷歌挖走了数十名人工智能专家,并在苏黎世创建了一个神秘的欧洲实验室。

关键字: 苹果 谷歌 实验室 AI
关闭
关闭