服务先自行处理异常
- 不要把所有的异常都交给框架处理
记录异常,并且抛出
RuntimeException
catch (IOException e) {
log.error("文件读取错误", e);
throw new RuntimeException("系统忙请稍后再试");
}
转换异常抛出
catch (IOException e) {
throw new RuntimeException("系统忙请稍后再试", e);
}
捕获异常一般做如下处理
- 记录日志
- 转换异常,类似上面的操作
- 不要用 RuntimeException
- 可以用 IllegalArgumentException、IllegalStateException、 UnsupportedOperationException 等
- 重试
- 降级,设置默认值
finally
- finally 抛出的异常会覆盖 try or catch 中的异常
finally 自行处理抛出的异常
public void test() throws Exception {
try {
log.info("try");
throw new RuntimeException("try");
} catch (Exception ex) {
e = ex;
} finally {
log.info("finally");
try {
throw new RuntimeException("finally");
} catch (Exception ex) {
// 这里
log.error("finally", ex);
}
}
throw e;
}
finally 使用
Exception#addSuppressed(otherEx);
,合并异常public void test() throws Exception {
Exception e = null;
try {
log.info("try");
throw new RuntimeException("try");
} catch (Exception ex) {
e = ex;
} finally {
log.info("finally");
try {
throw new RuntimeException("finally");
} catch (Exception ex) {
// 这里
if (e!= null) {
e.addSuppressed(ex);
} else {
e = ex;
}
}
}
throw e;
}
别把异常定义为静态变量
- 堆栈打印,会调用到定义静态异常遍历的地方
- Throwable 的 stacktrace 在 new 才初始化(调用
fillInStackTrace
),即 new 才获得异常抛出的栈- 除非手动再次调用
getStackTrace()
不会生成堆栈
提交线程池的任务出了异常会怎么样?
- 设置自定义的异常处理程序作为保底,声明线程池时使用
setUncaughtExceptionHandler
进行设置 - 设置全局的默认未捕获异常处理,使用
Thread.setDefaultUncaughtExceptionHandler()
进行设置
execute
- 内部 try catch
submit
- FutureTask 源码中,当执行任务出现异常后,异常存储到一个
outcome
字段 - 在调用
Future#get()
获取FutureTask
结果才会以ExcutionException
的形式抛出异常- 所以要对
get()
进行 try catch
- 所以要对
框架做兜底
- 不建议在框架层面进行异常的自动、统一处理,尤其不要随意捕获异常
- 对于自定义的业务异常,以 Warn 级别的日志记录异常以及当前 URL、执行方法等信息后,提取异常中的错误码和消息等信息,转换为合适的 API 包装体返回给 API 调用方;
- 对于无法处理的系统异常,以 Error 级别的日志记录异常和上下文信息(比如 URL、参数、用户 ID)后,转换为普适的“服务器忙,请稍后再试”异常信息,同样以 API 包装体返回给调用方。