异常处理(正确思路)

 一、不同业务层处理异常的思路

  • Repository 层出现异常或许可以忽略,或许可以降级,或许需要转化为一个友好的异常。如果一律捕获异常仅记录日志,很可能业务逻辑已经出错,而用户和程序本身完全感知不到
  • Service 层往往涉及数据库事务,出现异常同样不适合捕获,否则事务无法自动回滚。此外 Service 层涉及业务逻辑,有些业务逻辑执行中遇到业务异常,可能需要在异常后转入分支业务流程。如果业务异常都被框架捕获了,业务功能就会不正常
  • 如果下层异常上升到 Controller 层还是无法处理的话。Controller 层往往会给予用户友好提示,或是根据每一个 API 的异常表返回指定的异常类型,同样无法对所有异常一视同仁

 二、如何定义合格的统一异常处理

        对于自定义的业务异常,以 Warn 级别的日志记录异常以及当前 URL、执行方法等信息后,提取异常中的错误码和消息等信息,转换为合适的 API 包装体返回给 API 调用方;对于无法处理的系统异常,以 Error 级别的日志记录异常和上下文信息(比如 URL、参数、用户 ID)后,转换为普适的“服务器忙,请稍后再试”异常信息,同样以 API 包装体返回给调用方
@RestControllerAdvice
@Slf4j
public class RestControllerExceptionHandler {
    private static int GENERIC_SERVER_ERROR_CODE = 2000;
    private static String GENERIC_SERVER_ERROR_MESSAGE = "服务器忙,请稍后再试";
    @ExceptionHandler
    public APIResponse handle(HttpServletRequest req, HandlerMethod method, Exception ex) {
        if (ex instanceof BusinessException) {
            BusinessException exception = (BusinessException) ex;
            log.warn(String.format("访问 %s -> %s 出现业务异常!", req.getRequestURI(), method.toString()), ex);
            return new APIResponse(false, null, exception.getCode(), exception.getMessage());
        } else {
            log.error(String.format("访问 %s -> %s 出现系统异常!", req.getRequestURI(), method.toString()), ex);
            return new APIResponse(false, null, GENERIC_SERVER_ERROR_CODE, GENERIC_SERVER_ERROR_MESSAGE);
        }
    }
}

三、 其他注意点

禁止生吞异常

生吞异常也就是直接丢弃异常不记录、不抛出。不管是你认为多么不重要的异常,都不应该生吞,哪怕是一个日志也好,因为被生吞掉的异常一旦导致Bug,就很难在程序中找到蛛丝马迹,使得 Bug 排查工作难上加难

禁止丢弃异常的原始信息

丢弃了异常的原始信息容易让我们无法精准锁定异常

比如:

    private void readFile() throws IOException {
        Files.readAllLines(Paths.get("a_file"));
    }
    @GetMapping("wrong1")
    public void wrong1(){
        try {
            readFile();
        } catch (IOException e) {            
            //只保留了异常消息,栈没有记录
            log.error("文件读取错误, {}", e.getMessage());
            //原始异常信息丢失
            throw new RuntimeException("系统忙请稍后再试");
        }
    }
我们的日志就成了这个样子,谁能说这个是什么错吗?
[12:57:19.746] [http-nio-45678-exec-1] [ERROR] [.g.t.c.e.d.HandleExceptionController:35 ] - 文件读取错误 , a_file

 通常我们建议采用下面的方式记录异常信息

catch (IOException e) {
    log.error("文件读取错误", e);
    throw new RuntimeException("系统忙请稍后再试");
}

禁止抛出异常时不指定任何消息

throw new RuntimeException();
[13:25:18.031] [http-nio-45678-exec-3] [ERROR] [c.e.d.RestControllerExceptionHandler:24 ] - 访问 /handleexception/wrong3 -> org…..demo1.HandleExceptionController#wrong3(String) 出现系统异常!
java.lang.RuntimeException: null
...

 这里的 null 非常容易引起误解。按照空指针问题排查半天才发现,其实是异常的 message 为空 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值