Java-异常:不恰当的异常转换、不充分的日志记录、过度或不当的异常捕获

Java-异常:不恰当的异常转换、不充分的日志记录、过度或不当的异常捕获

异常相关概念可以参考:秋刀鱼不做梦-Java 异常处理详解

在这里,我重点针对异常不恰当的转换不充分的日志记录过度或不当的异常捕获的问题来说

一、前期准备

封装API的响应结果

@Data
@AllArgsConstructor
public class APIResponse<T> {
    private boolean success;
    private T data;
    private int code;
    private String message;
}

自定义异常

public class BusinessException extends RuntimeException {

    private int code;

    public BusinessException(String message, int code) {
        super(message);
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}

定义全局异常处理器

@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);
        }
    }
}

一个异常处理器,用于处理@RestController注解的控制器中抛出的异常。它使用@Slf4j注解来使用日志记录功能

  • @RestControllerAdvice注解表示这是一个全局异常处理类。
  • @ExceptionHandler注解表示该方法用于处理异常。
  • handle方法用于捕获异常,并根据异常类型返回不同的响应。
    • 如果异常是BusinessException类型,则记录日志并返回一个包含异常信息的APIResponse对象。
    • 如果异常不是BusinessException类型,则记录日志并返回一个包含通用错误信息的APIResponse对象。

controller

@Slf4j
@RestController
@RequestMapping("handleexception")
public class HandleExceptionController {
    
    //……
    
    private void readFile() throws IOException {
        Files.readAllLines(Paths.get("a_file"));
    }
}

二、案例分析

@GetMapping("exception")
public void exception(@RequestParam("business") boolean b) {
    if (b)
        throw new BusinessException("订单不存在", 2001);
    throw new RuntimeException("系统错误");
}

在这里插入图片描述

自定义异常生效

1、不恰当的异常转换

    @GetMapping("wrong1")
    public void wrong1() {
        try {
            readFile();
        } catch (IOException e) {
            throw new RuntimeException("系统忙请稍后再试");
        }
    }

在这里插入图片描述

问题将具体的IOException转换为非具体的RuntimeException,失去了原始异常的详细信息,不利于问题的定位和调试。应该尽可能保留或封装原始异常,以便于追踪错误源头

2、不充分日志记录

    @GetMapping("wrong2")
    public void wrong2() {
        try {
            readFile();
        } catch (IOException e) {
            log.error("文件读取错误, {}", e.getMessage());
            throw new RuntimeException("系统忙请稍后再试");
        }
    }

在这里插入图片描述

问题

  • 虽然记录了日志但异常转换仍存在问题:与wrong1类似,该方法虽然在抛出新异常前记录了详细的错误信息,但是最终抛出的是一个不包含原始异常信息的RuntimeException。这会导致堆栈跟踪信息丢失,不易于问题诊断。
  • 可以改进日志内容:虽然记录了错误信息,但是直接使用e.getMessage()可能不足以覆盖所有上下文信息,理想情况下应考虑记录更多上下文,如当前执行的操作、涉及的变量值等。

3、过度或不当的异常捕获

    @GetMapping("wrong3")
    public void wrong3(@RequestParam("orderId") String orderId) {
        try {
            readFile();
        } catch (Exception e) {
            log.error("文件读取错误", e);
            throw new RuntimeException();
        }
    }

在这里插入图片描述

问题

  • 过度捕获异常使用了非常广泛的Exception进行捕获,这意味着它会捕获所有类型的异常,包括那些可能本应由上层处理或根本就不应该被捕获的异常。这可能导致隐藏问题使得某些异常被不恰当地处理
  • 缺少异常信息抛出的RuntimeException没有提供任何具体信息,这对于调试和日志记录是非常不利的。至少应该提供一个有意义的错误消息
  • 日志记录不完整:虽然记录了错误日志,但是没有利用占位符插入异常信息(e)到日志消息中,这可能会遗漏重要的异常详情

上述的问题主要影响系统的可维护性和故障排查能力,所以我们需要更加细致地处理异常,保留异常链,以及提供充分的日志信息

三、正确处理方式

1、方式1

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

在这里插入图片描述

2、方式2

    @GetMapping("right2")
    public void right2() {
        try {
            readFile();
        } catch (IOException e) {
            throw new RuntimeException("系统忙请稍后再试", e);
        }
    }

在这里插入图片描述

方式1和方式2虽然表达不一样,但是都解决了:不恰当的异常转换、不充分的日志记录、过度或不当的异常捕获的问题,虽然代码简单,但是涉及异常操作时,也要引起重视,不然,出bug了,不好搞

  • 异常精确捕获,并精确处理,比方说上面right1和right2都是IOException
  • 获取更详细的异常日志,直接打印 e
  • 注意异常转换,丢失异常的上下
  • 37
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小孔靠得住

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值