业务、线程、虚拟机、通信框架等对异常的处理

一. 前言

异常是开发中,非常常见的。异常在不同的场景中,有不同的作用。本文就聊一聊,在 java 应用、线程、虚拟机、通信框架等场景中,对异常常见的处理方式。

二. 语言场景下的异常

首先是超类 java.lang.Throwable ,超类有两种类型的子类,一类是 java.lang.Error ,一类是 java.lang.Exception 。前者是用来描述 java 内部在运行时出现错误或者资源耗尽情况,不应该被 catch 捕获。后者分为两种情况,一种是 java.lang.RuntimeException 和其他实现了父类的异常。

RuntimeException 在被 throw 出去的时候,方法上不需要声明,直接就可以抛出。而其他受检异常需要声明抛出的异常类型。

比如 java.lang.String 的构造方法,就会根据不同的场景抛出不同种类的异常。
在构造 String 解码出现异常,会抛出受检异常 java.lang.IOException 的子类:

public String(byte bytes[], String charsetName) throws UnsupportedEncodingException { }

在使用带有 charset 的构造方法,并且参数为 null 时,不需要在方法上增加签名,直接抛出即可:

if (charset == null) throw new NullPointerException("charset");

一般来说,未受检异常的抛出场景是:错误的类型转换、数组访问越界、访问空指针等。受检异常的抛出场景是:试图在文件尾部后面读取数据、试图打开一个错误格式的 URL、试图根据给定的字符串查找 Class 对象而类不存在。

三. 业务场景下异常的处理

业务场景,一般是声明 BizException 继承 RuntimeException 。不同种类的异常,比如参数校验异常、数据库语句执行异常、第三方依赖异常等,还可以细分,最终由统一的异常处理类进行处理,然后返回特定的状态码。

阿里巴巴编码规范中,有这么几条异常异常处理要求很规范:

1.异常不要用来做流程控制,条件控制。
2.catch 时,分清稳定异常和不稳定异常,不稳定异常最好还进行细分,分别处理。
3.异常捕获了一定要处理,如果不处理一定不要捕获。
4.在调用 RPC、二方包、或者动态生成类等方法时,catch 时捕获 Throwable。

四. 线程对异常的处理

点开 java.lang.Thread#start() 方法,可以看到线程的执行,是调用了本地方法 :

private native void start0();

本地方法的执行过程,与 java 层面无关。虚拟机在执行线程中方法遇到异常并且未被捕获时,会调用 Thread#dispatchUncaughtException() 进行处理。如果线程没有重写这个方法,则虚拟机会一直向上抛出,直到被捕获或者终止线程。

主线程无法捕捉子线程的异常,但是主线程可以使用 FutureTask 作为钩子,在主线程中调用 get() 方法,这样就可以在主线程中捕捉异常。

在虚拟机中,每个含有 catch 语句的字节码中都附带了一个异常表,它包含每个异常 try 语句块的条目(entry)。每个条目都有四个信息,try 包裹的语句块的起点、try 包裹的语句块的终点、跳转的 pc 偏移位置、以及该异常类所在常量池中的索引。

如果在运行时抛出一个异常,虚拟机会按顺序找匹配的条目,如果匹配到,则把 程序计数器 偏转到上述 跳转的 pc 偏移位置上,然后继续执行指令。如果没有匹配到,则弹出当前栈帧,向上继续抛出异常,上级调用方会继续查找异常表。

五. 通信框架对异常的处理

Throwable 实现了 Serializable 接口,表明异常也是可以被序列化的。

RPC 框架的处理流程,就是把 java 类序列化后通过网络传输给调用方,调用方进行反序列化。

一般的 RPC 框架,比如 dubbo 和 feign ,都有一个统一的异常处理类。

dubbo 就是 com.alibaba.dubbo.rpc.filter.ExceptionFilter,这个类中对不同的异常有不同的处理方式,受检异常直接抛出,其他异常,原则上如果调用方是可以进行反序列化为 java对象 的就直接抛出,如果不能,则使用 RuntimeException 包裹后再抛出。

feign 默认是通过 http 协议进行网络传输,序列化方式是 json字符串。所以如果被调用方抛异常了,返回给调用方的,就不是异常对象,而是异常信息。springboot web 项目,异常时返回的信息就是 status、timestamp、message、error、path,仅通过这些信息,是没法反序列化成 java 对象的。所以 feign 的异常处理,是获取 message 信息,然后包装成 FeignException 返回出去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值