服务先自行处理异常

  • 不要把所有的异常都交给框架处理
  1. 记录异常,并且抛出 RuntimeException

    1. catch (IOException e) {
    2. log.error("文件读取错误", e);
    3. throw new RuntimeException("系统忙请稍后再试");
    4. }
  2. 转换异常抛出

    1. catch (IOException e) {
    2. throw new RuntimeException("系统忙请稍后再试", e);
    3. }

捕获异常一般做如下处理

  • 记录日志
  • 转换异常,类似上面的操作
    • 不要用 RuntimeException
    • 可以用 IllegalArgumentException、IllegalStateException、 UnsupportedOperationException 等
  • 重试
  • 降级,设置默认值

finally

  • finally 抛出的异常会覆盖 try or catch 中的异常
  1. finally 自行处理抛出的异常

    1. public void test() throws Exception {
    2. try {
    3. log.info("try");
    4. throw new RuntimeException("try");
    5. } catch (Exception ex) {
    6. e = ex;
    7. } finally {
    8. log.info("finally");
    9. try {
    10. throw new RuntimeException("finally");
    11. } catch (Exception ex) {
    12. // 这里
    13. log.error("finally", ex);
    14. }
    15. }
    16. throw e;
    17. }
  2. finally 使用 Exception#addSuppressed(otherEx); ,合并异常

    1. public void test() throws Exception {
    2. Exception e = null;
    3. try {
    4. log.info("try");
    5. throw new RuntimeException("try");
    6. } catch (Exception ex) {
    7. e = ex;
    8. } finally {
    9. log.info("finally");
    10. try {
    11. throw new RuntimeException("finally");
    12. } catch (Exception ex) {
    13. // 这里
    14. if (e!= null) {
    15. e.addSuppressed(ex);
    16. } else {
    17. e = ex;
    18. }
    19. }
    20. }
    21. throw e;
    22. }

别把异常定义为静态变量

  • 堆栈打印,会调用到定义静态异常遍历的地方
  • Throwable 的 stacktrace 在 new 才初始化(调用 fillInStackTrace ),即 new 才获得异常抛出的栈
    • 除非手动再次调用
    • getStackTrace() 不会生成堆栈

提交线程池的任务出了异常会怎么样?

  • 抛出异常的线程会退出,线程池需要重新创建线程

    设置异常处理程序

  • 默认实现是 ThreadGroup#uncaughtException()

  1. 设置自定义的异常处理程序作为保底,声明线程池时使用 setUncaughtExceptionHandler 进行设置
  2. 设置全局的默认未捕获异常处理,使用 Thread.setDefaultUncaughtExceptionHandler() 进行设置

execute

  • 内部 try catch

submit

  • FutureTask 源码中,当执行任务出现异常后,异常存储到一个 outcome 字段
  • 在调用 Future#get() 获取 FutureTask 结果才会以 ExcutionException 的形式抛出异常
    • 所以要对 get() 进行 try catch

框架做兜底

  • 不建议在框架层面进行异常的自动、统一处理,尤其不要随意捕获异常
  • 对于自定义的业务异常,以 Warn 级别的日志记录异常以及当前 URL、执行方法等信息后,提取异常中的错误码和消息等信息,转换为合适的 API 包装体返回给 API 调用方;
  • 对于无法处理的系统异常,以 Error 级别的日志记录异常和上下文信息(比如 URL、参数、用户 ID)后,转换为普适的“服务器忙,请稍后再试”异常信息,同样以 API 包装体返回给调用方。