springboot项目中如何优雅的实现异常处理

在项目中我们可以通过自定义异常类 QLException、全局异常处理器 GlobalExceptionHandler 和响应数据封装类 ResSerialization,我们实现了一个统一的异常处理机制。这种机制不仅提高了代码的整洁度和可维护性,还确保了系统的健壮性和用户体验的一致性。日志配置的合理使用,也为后续的故障排查提供了有力支持。


1. 自定义异常类 QLException

代码内容
@Data
public class QLException extends RuntimeException {
    private String msg;
    private int code = 1001;
    private int status = 404;

    public QLException(BaseExecptionEnum execptionEnum) {
        super(execptionEnum.getValue());
        this.status = execptionEnum.getStatus();
        this.code = execptionEnum.getCode();
        this.msg = execptionEnum.getValue();
    }

    public QLException(String msg) {
        super(msg);
        this.msg = msg;
        this.code = QLHttpStatus.NOT_FOUND.value();
    }

    public QLException(String msg, int code, int status) {
        super(msg);
        this.msg = msg;
        this.code = code;
        this.status = status;
    }

    public QLException(BaseExecptionEnum execptionEnum, Throwable err) {
        super(execptionEnum.getValue(), err);
        this.status = execptionEnum.getStatus();
        this.code = execptionEnum.getCode();
        this.msg = execptionEnum.getValue();
    }
}
逐行解析
  1. @Data:

    • 这是 Lombok 注解,自动生成 gettersettertoString 等方法,减少样板代码。
    @Data
    
  2. public class QLException extends RuntimeException:

    • 定义了一个自定义异常类 QLException,继承自 RuntimeException,因此它是一个运行时异常,不需要在方法签名中声明。
    public class QLException extends RuntimeException {
    
  3. private String msg;:

    • 定义一个字段 msg,用于存储异常的错误信息。
    private String msg;
    
  4. private int code = 1001;:

    • 定义一个字段 code,用于存储业务状态码,默认值为 1001
    private int code = 1001;
    
  5. private int status = 404;:

    • 定义一个字段 status,用于存储 HTTP 状态码,默认值为 404
    private int status = 404;
    
  6. public QLException(BaseExecptionEnum execptionEnum):

    • 构造方法,接收一个 BaseExecptionEnum 枚举对象。
    • super(execptionEnum.getValue());: 调用父类 RuntimeException 的构造方法,传入枚举中的错误信息。
    • this.status = execptionEnum.getStatus();: 从枚举中获取 HTTP 状态码并赋值给 status 字段。
    • this.code = execptionEnum.getCode();: 从枚举中获取业务状态码并赋值给 code 字段。
    • this.msg = execptionEnum.getValue();: 从枚举中获取错误信息并赋值给 msg 字段。
    public QLException(BaseExecptionEnum execptionEnum) {
        super(execptionEnum.getValue());
        this.status = execptionEnum.getStatus();
        this.code = execptionEnum.getCode();
        this.msg = execptionEnum.getValue();
    }
    
  7. public QLException(String msg):

    • 构造方法,接收一个错误信息字符串。
    • super(msg);: 调用父类 RuntimeException 的构造方法,传入错误信息。
    • this.msg = msg;: 将传入的错误信息赋值给 msg 字段。
    • this.code = QLHttpStatus.NOT_FOUND.value();: 设置默认的业务状态码为 404
    public QLException(String msg) {
        super(msg);
        this.msg = msg;
        this.code = QLHttpStatus.NOT_FOUND.value();
    }
    
  8. public QLException(String msg, int code, int status):

    • 构造方法,接收错误信息、业务状态码和 HTTP 状态码。
    • super(msg);: 调用父类 RuntimeException 的构造方法,传入错误信息。
    • this.msg = msg;: 将传入的错误信息赋值给 msg 字段。
    • this.code = code;: 将传入的业务状态码赋值给 code 字段。
    • this.status = status;: 将传入的 HTTP 状态码赋值给 status 字段。
    public QLException(String msg, int code, int status) {
        super(msg);
        this.msg = msg;
        this.code = code;
        this.status = status;
    }
    
  9. public QLException(BaseExecptionEnum execptionEnum, Throwable err):

    • 构造方法,接收一个 BaseExecptionEnum 枚举对象和一个 Throwable 异常对象。
    • super(execptionEnum.getValue(), err);: 调用父类 RuntimeException 的构造方法,传入枚举中的错误信息和原始异常。
    • this.status = execptionEnum.getStatus();: 从枚举中获取 HTTP 状态码并赋值给 status 字段。
    • this.code = execptionEnum.getCode();: 从枚举中获取业务状态码并赋值给 code 字段。
    • this.msg = execptionEnum.getValue();: 从枚举中获取错误信息并赋值给 msg 字段。
    public QLException(BaseExecptionEnum execptionEnum, Throwable err) {
        super(execptionEnum.getValue(), err);
        this.status = execptionEnum.getStatus();
        this.code = execptionEnum.getCode();
        this.msg = execptionEnum.getValue();
    }
    

2. 全局异常处理器 GlobalExceptionHandler

代码内容
@RestControllerAdvice("com.qingluo.pay")
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public Serializable handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException e) {
        log.error("不支持的媒体类型异常 --> {} ", e);
        return ResSerialization.error(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), "不支持的媒体类型");
    }

    @ExceptionHandler
    public Serializable handle(IllegalArgumentException e) {
        if (ObjectUtil.isNotEmpty(e.getMessage())) {
            log.error("Assert参数断言异常 --> {} ", e);
        }
        return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), "参数异常");
    }

    @ExceptionHandler
    public Serializable handle(ValidationException e) {
        List<String> errors = null;
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) e;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            errors = violations.stream()
                    .map(ConstraintViolation::getMessage).collect(Collectors.toList());
        }
        if (ObjectUtil.isNotEmpty(e.getCause())) {
            log.error("参数校验失败异常 ->  {} ", e);
        }
        return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), errors.toString());
    }

    @ExceptionHandler
    public Serializable handle(QLException e) {
        if (ObjectUtil.isNotEmpty(e.getStatus())) {
            log.error("自定义异常 --> {} ", e.getMsg());
        }
        return ResSerialization.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler
    public Serializable handle(Exception e) {
        if (ObjectUtil.isNotEmpty(e.getCause())) {
            log.error("未知异常 --> {} ", e.getMessage());
        }
        return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), e.getMessage());
    }
}
逐行解析
  1. @RestControllerAdvice("com.qingluo.pay"):

    • 这是一个 Spring 注解,表示这是一个全局异常处理器,处理 com.qingluo.pay 包下的所有控制器抛出的异常。
    @RestControllerAdvice("com.qingluo.pay")
    
  2. @Slf4j:

    • 这是 Lombok 注解,自动生成日志对象 log,用于记录日志。
    @Slf4j
    
  3. @ExceptionHandler(HttpMediaTypeNotAcceptableException.class):

    • 这是一个异常处理方法,专门处理 HttpMediaTypeNotAcceptableException 异常。
    • log.error("不支持的媒体类型异常 --> {} ", e);: 记录异常日志。
    • return ResSerialization.error(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), "不支持的媒体类型");: 返回一个错误响应,HTTP 状态码为 415,错误信息为 “不支持的媒体类型”。
    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public Serializable handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException e) {
        log.error("不支持的媒体类型异常 --> {} ", e);
        return ResSerialization.error(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), "不支持的媒体类型");
    }
    
  4. @ExceptionHandler:

    • 这是一个通用的异常处理方法,处理 IllegalArgumentException 异常。
    • if (ObjectUtil.isNotEmpty(e.getMessage())) { ... }: 如果异常信息不为空,则记录日志。
    • return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), "参数异常");: 返回一个错误响应,HTTP 状态码为 400,错误信息为 “参数异常”。
    @ExceptionHandler
    public Serializable handle(IllegalArgumentException e) {
        if (ObjectUtil.isNotEmpty(e.getMessage())) {
            log.error("Assert参数断言异常 --> {} ", e);
        }
        return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), "参数异常");
    }
    
  5. @ExceptionHandler:

    • 这是一个通用的异常处理方法,处理 ValidationException 异常。
    • List<String> errors = null;: 定义一个列表,用于存储校验失败的错误信息。
    • if (e instanceof ConstraintViolationException) { ... }: 如果异常是 ConstraintViolationException 类型,则提取校验失败的具体信息。
    • return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), errors.toString());: 返回一个错误响应,HTTP 状态码为 400,错误信息为校验失败的具体信息。
    @ExceptionHandler
    public Serializable handle(ValidationException e) {
        List<String> errors = null;
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) e;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            errors = violations.stream()
                    .map(ConstraintViolation::getMessage).collect(Collectors.toList());
        }
        if (ObjectUtil.isNotEmpty(e.getCause())) {
            log.error("参数校验失败异常 ->  {} ", e);
        }
        return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), errors.toString());
    }
    
  6. @ExceptionHandler:

    • 这是一个通用的异常处理方法,处理 QLException 异常。
    • if (ObjectUtil.isNotEmpty(e.getStatus())) { ... }: 如果异常的状态码不为空,则记录日志。
    • return ResSerialization.error(e.getCode(), e.getMessage());: 返回一个错误响应,业务状态码和错误信息来自异常对象。
    @ExceptionHandler
    public Serializable handle(QLException e) {
        if (ObjectUtil.isNotEmpty(e.getStatus())) {
            log.error("自定义异常 --> {} ", e.getMsg());
        }
        return ResSerialization.error(e.getCode(), e.getMessage());
    }
    
  7. @ExceptionHandler:

    • 这是一个通用的异常处理方法,处理所有未知异常。
    • if (ObjectUtil.isNotEmpty(e.getCause())) { ... }: 如果异常的原始原因不为空,则记录日志。
    • return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), e.getMessage());: 返回一个错误响应,HTTP 状态码为 400,错误信息为异常的具体信息。
    @ExceptionHandler
    public Serializable handle(Exception e) {
        if (ObjectUtil.isNotEmpty(e.getCause())) {
            log.error("未知异常 --> {} ", e.getMessage());
        }
        return ResSerialization.error(HttpStatus.BAD_REQUEST.value(), e.getMessage());
    }
    

3. 总结

通过逐行解析自定义异常类 QLException 和全局异常处理器 GlobalExceptionHandler,我们深入理解了 Spring Boot 中异常处理的实现细节。这种机制不仅提高了代码的可维护性,还为用户提供了友好的错误提示。希望本文对您有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

攒了一袋星辰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值