异常处理(正确思路)

简介: 异常处理(正确思路)

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

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 为空  

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
|
网络架构
udp的简单整理
udp的简单整理
662 0
|
应用服务中间件 nginx
iframe嵌套其他网站提示连接被拒绝
iframe嵌套其他网站提示连接被拒绝
2266 0
|
XML JSON 安全
借助API接口实现自营商城上货采集,无货源模式采集商品
在无货源模式的自营商城中,通过API接口实现商品采集是一个高效且灵活的方式。这种方式允许商家直接从供应商或其他电商平台的API接口中获取商品信息,然后将这些信息导入到自己的商城中,无需自己拥有实际的库存。
|
5月前
|
域名解析 弹性计算 网络协议
阿里云ipv6设置,免费将ipv4网站升级为ipv6
阿里云ipv6设置,免费将ipv4网站升级为ipv6,网站ipv6设置
1890 0
|
消息中间件 机器学习/深度学习 人工智能
AI赋能运维:实现运维任务的智能化自动分配
AI赋能运维:实现运维任务的智能化自动分配
1002 24
|
SQL BI 网络安全
SQL Server2014的安装与配置
SQL Server2014的安装与配置
938 0
|
弹性计算 运维 负载均衡
构建高可用性的分布式系统:技术与策略
【7月更文挑战第1天】构建高可用分布式系统涉及负载均衡、容错处理和数据一致性等关键技术,遵循冗余、模块化及异步设计原则,并通过监控告警、自动化运维和弹性伸缩策略确保稳定性。
|
API 数据安全/隐私保护
SpringSecurity结合JwtToken验证(后端部分)
SpringSecurity结合JwtToken验证(后端部分)
387 0
SpringSecurity结合JwtToken验证(后端部分)
|
Java 关系型数据库 MySQL
Spring Boot中集成MySQL数据库的步骤和技巧
Spring Boot中集成MySQL数据库的步骤和技巧
|
关系型数据库 MySQL Shell
黑马程序员2024最新SpringCloud微服务开发与实战 个人学习心得、踩坑、与bug记录Day2 全网最快最全(下)
黑马程序员2024最新SpringCloud微服务开发与实战 个人学习心得、踩坑、与bug记录Day2 全网最快最全(下)
742 0