4. 异常类层次结构设计:基础异常类、业务异常类、系统异常类、自定义异常类的继承体系

在企业级系统中,异常类的层次结构设计是错误处理骨架的基石。一个设计良好的异常继承体系,能够帮助开发团队快速定位问题根因、统一错误响应格式、降低跨模块耦合。本章将深入剖析四层异常体系的设计原理与工程落地细节。

4.1 四层异常体系总览

我们采用经典的“基础 → 业务 → 系统 → 自定义”四层继承模型。每一层承担不同的职责,从最顶层的通用契约到底层的具体场景实现,形成完整的错误分类树。

异常类层次结构设计(四层继承体系) BaseException 基础异常类(顶层抽象) BusinessException 业务异常类(可预知业务规则) SystemException 系统异常类(不可预知技术故障) OrderBizException 订单业务异常 UserBizException 用户业务异常 DbSysException 数据库系统异常 InsufficientStockEx 库存不足 InvalidParamEx 参数校验失败 ConnTimeoutEx 连接超时 DiskFullEx 磁盘空间不足 说明:箭头方向表示继承关系(extends),子类继承父类的错误码、消息、堆栈等核心属性

4.2 基础异常类(BaseException)

BaseException 是整个异常体系的根节点,通常继承自 RuntimeException(非受检异常)。它定义了所有异常必须携带的通用契约:错误码、错误消息、时间戳、异常级别。

设计原则:基础异常类不包含任何业务语义,只提供最底层的属性容器和构造方法。所有子类必须通过 super() 调用父类构造器。
public abstract class BaseException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    private final String errorCode;      // 错误码,如 "BIZ-0001"
    private final String errorMessage;   // 错误描述
    private final long timestamp;        // 异常发生时间戳
    private final ErrorLevel level;      // 异常级别:WARN / ERROR / FATAL

    // 核心构造器
    public BaseException(String errorCode, String errorMessage, ErrorLevel level) {
        super(errorMessage);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
        this.timestamp = System.currentTimeMillis();
        this.level = level;
    }

    // 支持原始异常的构造器
    public BaseException(String errorCode, String errorMessage, ErrorLevel level, Throwable cause) {
        super(errorMessage, cause);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
        this.timestamp = System.currentTimeMillis();
        this.level = level;
    }

    // getter 方法
    public String getErrorCode() { return errorCode; }
    public String getErrorMessage() { return errorMessage; }
    public long getTimestamp() { return timestamp; }
    public ErrorLevel getLevel() { return level; }
}

4.3 业务异常类(BusinessException)

BusinessException 继承自 BaseException,代表可预知的业务规则违反。例如:库存不足、订单状态不允许取消、用户余额不足等。这类异常通常需要在前端展示友好的提示信息,并触发特定的业务补偿流程。

属性 说明 示例值
errorCode 业务错误码,前缀 BIZ BIZ-1001
errorMessage 面向用户的友好提示 “商品库存不足,当前仅剩 2 件”
level 通常为 WARN ErrorLevel.WARN
cause 可选,业务异常一般不包装底层技术异常 null
public class BusinessException extends BaseException {
    public BusinessException(String errorCode, String errorMessage) {
        super(errorCode, errorMessage, ErrorLevel.WARN);
    }

    public BusinessException(String errorCode, String errorMessage, Throwable cause) {
        super(errorCode, errorMessage, ErrorLevel.WARN, cause);
    }
}
工程建议:业务异常不要包装底层 SQL 或网络异常,否则会暴露技术细节。如果业务操作因技术故障失败,应抛出 SystemException 而非 BusinessException。

4.4 系统异常类(SystemException)

SystemException 同样继承自 BaseException,代表不可预知的技术故障。例如:数据库连接池耗尽、Redis 超时、磁盘 I/O 错误、第三方服务不可用。这类异常通常需要触发告警、记录完整堆栈、并执行降级或熔断策略。

属性 说明 示例值
errorCode 系统错误码,前缀 SYS SYS-2001
errorMessage 面向运维的技术描述 “数据库连接池已满,等待超时 30s”
level 通常为 ERROR 或 FATAL ErrorLevel.ERROR
cause 必须包装原始技术异常 new SQLException(...)
public class SystemException extends BaseException {
    public SystemException(String errorCode, String errorMessage, Throwable cause) {
        super(errorCode, errorMessage, ErrorLevel.ERROR, cause);
    }

    public SystemException(String errorCode, String errorMessage, ErrorLevel level, Throwable cause) {
        super(errorCode, errorMessage, level, cause);
    }
}
关键区别:BusinessException 的 level 为 WARN,不触发告警;SystemException 的 level 为 ERROR 或 FATAL,必须触发告警并记录全堆栈。这是区分“业务提示”和“系统故障”的核心分界线。

4.5 自定义异常类(Custom Exception)

自定义异常类位于继承体系的第三层和第四层,是实际项目中使用频率最高的异常类型。它们继承自 BusinessException 或 SystemException,为特定业务模块或技术组件提供精确的错误语义。

4.5.1 自定义业务异常示例

// 订单模块业务异常
public class OrderBizException extends BusinessException {
    public static final String INSUFFICIENT_STOCK = "BIZ-1001";
    public static final String ORDER_STATUS_INVALID = "BIZ-1002";
    public static final String PAYMENT_TIMEOUT = "BIZ-1003";

    public OrderBizException(String errorCode, String errorMessage) {
        super(errorCode, errorMessage);
    }

    // 静态工厂方法,简化调用
    public static OrderBizException insufficientStock(String skuId, int available) {
        return new OrderBizException(INSUFFICIENT_STOCK,
            String.format("商品 [%s] 库存不足,当前可用库存: %d", skuId, available));
    }
}

// 用户模块业务异常
public class UserBizException extends BusinessException {
    public static final String USER_NOT_FOUND = "BIZ-2001";
    public static final String PASSWORD_ERROR = "BIZ-2002";
    public static final String ACCOUNT_LOCKED = "BIZ-2003";

    public UserBizException(String errorCode, String errorMessage) {
        super(errorCode, errorMessage);
    }
}

4.5.2 自定义系统异常示例

// 数据库系统异常
public class DbSysException extends SystemException {
    public static final String CONNECTION_TIMEOUT = "SYS-1001";
    public static final String DEADLOCK_DETECTED = "SYS-1002";
    public static final String QUERY_TIMEOUT = "SYS-1003";

    public DbSysException(String errorCode, String errorMessage, Throwable cause) {
        super(errorCode, errorMessage, cause);
    }
}

// 缓存系统异常
public class CacheSysException extends SystemException {
    public static final String REDIS_UNAVAILABLE = "SYS-2001";
    public static final String CACHE_KEY_NOT_FOUND = "SYS-2002";

    public CacheSysException(String errorCode, String errorMessage, Throwable cause) {
        super(errorCode, errorMessage, cause);
    }
}

4.6 异常级别枚举(ErrorLevel)

异常级别是异常体系中的关键元数据,决定了异常被捕获后的处理策略。我们定义三个级别:

级别 枚举值 处理策略 典型场景
警告 WARN 返回友好提示,不告警,不记录堆栈 参数校验失败、库存不足
错误 ERROR 记录完整堆栈,触发告警,执行降级 数据库超时、第三方服务不可用
致命 FATAL 记录堆栈,触发紧急告警,可能重启服务 磁盘空间满、OOM、配置中心不可用
public enum ErrorLevel {
    WARN(1, "警告"),
    ERROR(2, "错误"),
    FATAL(3, "致命");

    private final int code;
    private final String description;

    ErrorLevel(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public int getCode() { return code; }
    public String getDescription() { return description; }
}

4.7 异常类设计最佳实践

核心原则:异常类层次结构的设计目标是“精确分类 + 统一契约”,而不是“无限细分”。每增加一个自定义异常类,都应该有明确的业务或技术语义区分。
  • 原则一:基础异常类必须抽象,禁止直接实例化 BaseException。所有异常必须通过子类抛出。
  • 原则二:业务异常和系统异常严格分离。业务异常不包装技术异常,系统异常必须包装原始异常。
  • 原则三:自定义异常类使用静态工厂方法代替多参数构造器,提高代码可读性。
  • 原则四:错误码采用统一前缀(BIZ / SYS)加数字编号,便于日志检索和监控告警。
  • 原则五:异常类必须实现 serialVersionUID,避免跨 JVM 反序列化失败。

4.8 异常类使用示例

// 业务层抛出业务异常
public void deductStock(String skuId, int quantity) {
    int available = stockRepository.getAvailable(skuId);
    if (available < quantity) {
        throw OrderBizException.insufficientStock(skuId, available);
    }
    // 正常扣减逻辑...
}

// 基础设施层抛出系统异常
public Connection getConnection() {
    try {
        return dataSource.getConnection(30, TimeUnit.SECONDS);
    } catch (SQLTimeoutException e) {
        throw new DbSysException(DbSysException.CONNECTION_TIMEOUT,
            "数据库连接获取超时,等待 30s 无响应", e);
    }
}

// 全局异常处理器统一捕获
@ExceptionHandler(BaseException.class)
public ResponseEntity<ErrorResponse> handleBaseException(BaseException ex) {
    ErrorResponse response = new ErrorResponse(
        ex.getErrorCode(),
        ex.getErrorMessage(),
        ex.getTimestamp()
    );
    HttpStatus status = (ex instanceof SystemException)
        ? HttpStatus.INTERNAL_SERVER_ERROR
        : HttpStatus.BAD_REQUEST;
    return ResponseEntity.status(status).body(response);
}
工程落地检查清单:
  • ✓ 所有异常类是否继承自 BaseException?
  • ✓ 业务异常是否避免了包装技术异常?
  • ✓ 系统异常是否都包装了原始 cause?
  • ✓ 错误码是否遵循了统一前缀规范?
  • ✓ 全局异常处理器是否区分了 WARN 和 ERROR 级别?