服务先自行处理异常
- 不要把所有的异常都交给框架处理
记录异常,并且抛出
RuntimeExceptioncatch (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 包装体返回给调用方。
