异常统一处理:BusinessException(自定义业务异常)

一、引言

本篇内容是“异常统一处理”系列文章的重要组成部分,主要聚焦于对 BusinessException 的原理解析与异常处理机制,并给出测试案例。

二、业务异常原理

在Java企业级应用开发中,BusinessException 是一种常见的自定义异常类型,它通常继承自 java.lang.RuntimeException 类。这种异常专门用于表示业务逻辑层( BLL )出现的错误或不符合预期的情况,这些情况并不属于系统内部错误,但会导致当前业务流程无法正常完成。

由各种业务规则导致的异常情况,与系统级别的异常(如 IOException、NullPointerException等)不同,它们是业务逻辑上的错误,例如用户输入非法(如登录时的用户名密码错误)、订单状态非法操作、库存不足无法完成购买等情况。

设计 BusinessException 的主要目的是为了统一处理业务相关的错误,并方便异常传播时携带特定的业务上下文信息,如错误码、错误消息等。通过抛出 BusinessException,开发者可以清晰地区分并优雅地处理那些由应用程序业务规则违反导致的问题,而不是因编程错误或系统故障引起的异常。

在实际使用中,当业务执行过程中出现错误时(如用户输入非法、资源不足、权限不够等情况),可抛出此异常,并附带相应的错误码和详细错误信息,这样上层服务或者全局异常处理器就能根据异常类型和错误码来采取相应的恢复措施,比如记录日志、向客户端返回友好的提示信息以及进行事务回滚等操作。

三、业务异常代码

BusinessException

package com.example.core.model;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

/**
 * 业务异常
 */
@Getter
@Schema(name = "业务异常", description = "业务异常")
public class BusinessException extends RuntimeException {

    @Schema(description = "用户提示", example = "操作成功!")
    private final String userMessage;

    /**
     * 错误码<br>
     * 调用成功时,为 null。<br>
     * 示例:10001
     */
    @Schema(description = "错误码")
    private final String errorCode;

    /**
     * 错误信息<br>
     * 调用成功时,为 null。<br>
     * 示例:"验证码无效"
     */
    @Schema(description = "错误信息")
    private final String errorMessage;


    public BusinessException(ErrorEnum errorEnum) {
        super(String.format("错误码:[%s],错误信息:[%s],用户提示:[%s]", errorEnum.name(), errorEnum.getMessage(), errorEnum.getMessage()));
        this.userMessage = errorEnum.getMessage();
        this.errorCode = errorEnum.name();
        this.errorMessage = errorEnum.getMessage();
    }


    public BusinessException(String userMessage, String errorCode, String errorMessage) {
        super(String.format("错误码:[%s],错误信息:[%s],用户提示:[%s]", errorCode, errorMessage, userMessage));
        this.userMessage = userMessage;
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

}

ErrorEnum

下文为错误枚举的代码,本代码仅为示例,具体的错误枚举类型,应该按照公司的开发标准制定。

package com.example.core.model;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 错误枚举
 */
@Getter
@AllArgsConstructor
public enum ErrorEnum {

    A0001("用户端错误 "), // 一级宏观错误码


    // ------------------------------------------------------------------------------- //
    A0100("用户注册错误"), // 二级宏观错误码

    A0101("用户未同意隐私协议"),
    A0102("注册国家或地区受限"),

    A0110("用户名校验失败"),
    A0111("用户名已存在"),
    A0112("用户名包含敏感词"),
    A0113("用户名包含特殊字符"),


    // ------------------------------------------------------------------------------- //
    A0200("用户登录异常"), // 二级宏观错误码

    A0201("用户账户不存在"),
    A0202("用户账户被冻结"),
    A0203("用户账户已作废"),

    A0210("用户密码错误"),
    A0211("用户输入密码错误次数超限"),


    // ------------------------------------------------------------------------------- //
    A0400("用户请求参数错误"), // 二级宏观错误码

    A0420("请求参数值超出允许的范围"),
    A0421("参数格式不匹配"),
    A0422("地址不在服务范"),
    A0423("时间不在服务范围"),
    A0424("金额超出限制"),
    A0425("数量超出限制"),
    A0426("请求批量处理总个数超出限制"),
    A0427("请求 JSON 解析失败");


    private final String message;

}

四、异常处理代码

在Spring Boot应用中,我们可以通过使用@ExceptionHandler注解来捕获并处理BusinessException异常。

4.1 异常处理示意图

在这里插入图片描述

4.2 异常处理核心代码

package com.example.core.advice;

import com.example.core.model.BusinessException;
import com.example.core.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 业务异常处理
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Void> handle(BusinessException e, HandlerMethod handlerMethod) {
        // BusinessException(自定义业务异常)的处理逻辑,比如:记录日志等逻辑。
        return Result.fail(e.getUserMessage(), e.getErrorCode(), e.getErrorMessage());
    }

}

上述代码中,当出现BusinessException异常时,系统将返回一个状态码为400(Bad Request)的结果,并附带具体的错误信息。

五、测试案例

5.1 测试代码

    @GetMapping("business_exception")
    @Operation(summary = "业务异常", description = "测试 “业务异常”:直接将业务异常抛出,由 “异常统一处理” 模块来进行处理,将错误信息返回给前端。")
    public void throwBusinessException() {
        throw new BusinessException(ErrorEnum.A0210);
    }

5.2 未处理异常时的报错

请求响应

在这里插入图片描述

控制台的错误日志

在这里插入图片描述

5.3 已处理异常时的响应

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

宋冠巡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值