《全局异常捕获》劝劝潘子吧,别再用trycatch来处理异常了

Posted 洛 神

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《全局异常捕获》劝劝潘子吧,别再用trycatch来处理异常了相关的知识,希望对你有一定的参考价值。

人世仙家本自殊,何须相见向中途。惊鸿瞥过游龙去,漫恼陈王一事无。
嗨,大家好,我是洛神,性别男。一个来自快乐星球的程序员
欢迎大家专注我的公众号【程序员洛神】,不仅分享技术,还会分享生活趣事、体育。

前言

前几天闲来无事看之前的项目,发现项目中对异常问题的处理非常不优雅,有些接口中为了处理异常使用的trycatch语句占用了一半的代码,读起来灰常头疼,所以阿洛就用一招武林中失传已久的《全局异常捕获大法》一顿输出,处理完后的代码逻辑结构看起来很是清晰,优雅的yaPi~

啥是全局异常捕获?

全局异常捕获就是定一个全局异常捕获类(通常都是使用Spring自带的增强注解@ControllerAdvice),所有抛出的异常都会被捕获到(这个地方不严谨,应该是根据ExceptionHanler中定义的捕获范围来确定),然后根据捕获类中的处理逻辑对异常进行处理(一般都是返回前端一个封装好的实体类,里面包含msg,code等信息)。

说到这里了,阿洛就顺便带大家复习下异常相关的内容:

先看下异常类的大家庭(RunTimeException的子类太多,没有全部查出来,只找了几个常见的异常类,重点看整体结构关系)

Error大部分问题都是由于虚拟机问题造成的异常。这种问题是无法通过代码层面进行捕获或者处理的。

平时我们开发中打交道最多的就是Exception了,它具体分为RunTimeException(运行时异常)和IOException异常(其他异常)。
运行时异常我们是可以通过全局异常捕获来统一处理的,但是对于其他异常,Java是强制要求我们必须使用TryCatch或Throw来解决异常的(例如文件流处理啊,JDBC建立连接啊这些)

这里可能会有人急眼了,NND!好不容易学会了个全局异常捕获,结果这也不能用,那也不能用,劳资不学了!
(╯‵□′)╯︵┻━┻

别着急啊傻瓜,咱们这个全局异常捕获,本来就是用来处理运行时异常的问题的,因为大部分抛异常的情况都是运行时的异常。

为啥要用全局异常捕获处理?

全局异常捕获处理的好处有几个:
1.避免代码中出现大量的try catch,影响代码可读性。
2.减少重复开发代码量。

第一点我相信大家是可以理解的,因为我相信很多人都有被try catch折磨的经历,一个50行代码的接口,里面30行是try catch,自己看着糟心,review的时候领导起了杀心。用了全局异常捕获后,对于运行时的异常,我们不需要刻意的对代码进行异常处理,只需要在全局异常类中定义好相关的异常的处理逻辑,就可以放心的将异常抛出去了。

List lists = null;
System.out.println(lists.get(0));

这个代码如果正常运行,会出现什么,前端会直接返回一个nullPointerException,用户看见之后一脸懵逼。

为了良好的用户体验,初级开发工程师小张做了一下处理:

  List lists = null;
  try
       System.out.println(lists.get(0));
      catch (Exception e)
        MyRest myRest = new MyRest();
        myRest.setMsg("尊敬的用户,由于您不是VIP客户,无法调用该接口,请充值后重试");
        myRest.setHttpStatus(500);
        myRest.setIdentifier("-999");
        return myRest;
        

运行结果:

吆西!不仅完美解决了异常处理问题,而且还给公司带来了收益,这代码提交上去,明天我是不是就得当leader了,我擦,我第一次当领导,是不是得上去讲点啥?要是员工们不服从,我该咋办啊。。。。

小张激动的在钉钉上跟领导说:领导,问题修复好了。 领导:嗯,对了,还有一个地方类似的问题,你也给修改一下吧,完事一块提交给我。

小张:这尼玛不是送分题吗,这次升职加薪稳了!

小张翘起二郎腿,找到问题所在地,看了下代码:

呵,原来是数组越界的问题,简简单单

完成!提交!
领导review代码后:很好,小张,你被解雇了。

欸嘿?为啥嘞?没啥毛病啊,正确处理了潜在的异常问题,为啥领导还解雇我,肯定是担心我哪天骑到他头上,哼

小张还没有意识到事情的严重性,问题是解决了,但是回头看一下代码呢

20+行的代码,真正的业务代码有多少?不到5行,剩下的15行都是为了处理这个异常,这只是举例,真实业务环境下,一个接口里如果牵扯到了多个业务交互,你不得整个几千行?多吓人,review的时候不用看别的了,光看try catch就中了。烦不烦。

小张被开除的根本原因,还是因为他把握不住,不够优雅,不懂得好好反思,耗子尾汁。
那么,既然小张的这种写法有问题,那么我们看看公司的顶级架构师是怎么优化这个问题的?


/**
*全局异常捕获类
@author 公司顶级架构师
@date  小张被开除的那一天
*/
@ControllerAdvice
public class GlobalExceptionHandler 
 
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Rest handle(HttpServletRequest request, Exception e) 
    	//这里是举例 所以代码简化
        //真实项目中,不建议使用魔法值,所有下面这种定义常量,建议使用枚举类处理
        String errMsg = "未知错误";
        String errCode = "-999";
        int httpStatus = 500;
        if(e instanceof NullPointerException)
        	errMsg = "由于这个接口是顶级架构师开发的,所以VIP开通费用提高500元";
        
        else if (e instanceof IndexOutOfBoundsException)
        	errMsg = "由于这个接口是顶级架构师开发的,同样,升级超级VIP需要额外支付1000元";
        
        else 
            errMsg = e.toString();
        
        MyRest rest = new MyRest();
        rest.setIdentifier(errCode);
        rest.setHttpStatus(httpStatus);
        rest.setMsg(errMsg);
        return rest;
      

欸嘿,看起来有点意思的感jio了,那么这么做之后的效果如何呢?


同样是这两个问题,我们看一下不用trycatch包裹,执行后的效果如何?



欸嘿,惊不惊喜?意不意外?满足了同样的要求,我的业务代码没有任何改变,这种操作就是类似于SpringAOP的思想,非业务侵入式功能,在不改变原有业务代码的情况下,对代码进行增强。炫酷吊炸天有木有。
而且,有没有发现,上面我提到的使用全局捕获异常的好处里的第二条:减少重复开发代码量,相信大家也看到很明白了把,我只需要定义一次返回的格式即可。

好了,看完了顶级架构师的代码了,我们来说下是如何实现这个功能的:

其实也没有神秘的地方,能实现这个功能,主要是依赖于Spring为我们提供的注解@ControllerAdvice+@ExceptionHandler

@ControllerAdvice的作用
1.全局异常处理
2.全局数据绑定
3.全局数据预处理

使用也很简单,我们只需要建立一个专门的异常捕获的类,然后在类上面加上注解@ControllerAdvice。
这个注解的意义就是告诉Spring,所有你管理的bean里,只要抛出异常了,没有被trycatch处理的,统统都交给我就中了。

然后我们需要定义一个处理异常的方法,在方法上加上注解@ExceptionHandler 这个注解有个属性,需要定义异常的类型,根据自己需要来,如果你指定了某个异常类,那么就只有这个异常才会被全局捕获到。

具体你想把异常处理成啥样,那你就随便吧,根据你公司实际业务来,或者看你心情也可以。一般的话我们都是会将异常结果处理后根据格式规范返回给前端。

结尾

好了,这个就是全局异常捕获的一些知识了,我个人还是建议大家多去做这种类型的技术的锻炼的,我们要学会对整个系统进行把控,做任何事情都要考虑整个系统的性能和效果,这样才能快速提升自己的能力,从而达到顶级架构师的高度!全局异常捕获一个很小的知识点,但是它代表的是一种能力,代表开发人员对于开发的理解能力和抽象能力,这玩意看不见摸不着,自己多品品。

人世仙家本自殊,何须相见向中途。惊鸿瞥过游龙去,漫恼陈王一事无。我是洛神,我们下期见。

以上是关于《全局异常捕获》劝劝潘子吧,别再用trycatch来处理异常了的主要内容,如果未能解决你的问题,请参考以下文章

《全局异常捕获》劝劝潘子吧,别再用trycatch来处理异常了

spring处理异常的办法

springboot 全局捕获异常

.Net 6.0全局异常捕获

SpringBoot:如何优雅地处理全局异常?

全局捕获所有可能的android异常并重新加载应用程序