Java内置异常虽覆盖基础错误,却难以精准表达复杂业务逻辑的失败语义。自定义异常通过继承Exception或RuntimeException,封装特定错误码、上下文信息及处理逻辑,大幅提升代码可读性与可维护性。
一、为何需要自定义异常?
当系统抛出IllegalArgumentException时,你是否曾困惑:“究竟哪个参数非法?为何非法?” 内置异常的泛化性在复杂业务中成为调试噩梦。自定义异常直击痛点:
// 业务专属异常:余额不足
public class InsufficientBalanceException extends RuntimeException {
private final String accountId;
private final BigDecimal requiredAmount;
public InsufficientBalanceException(String accountId, BigDecimal requiredAmount) {
super(String.format("账户 %s 余额不足,需 %.2f 元", accountId, requiredAmount));
this.accountId = accountId;
this.requiredAmount = requiredAmount;
}
// 可扩展错误码、恢复逻辑等
}
二、创建自定义异常三步法
- 继承根基类:
· 受检异常 → extends Exception
· 非受检异常 → extends RuntimeException - 构造器设计:
· 至少实现带String message的构造器
· 支持传递原始异常(Throwable cause) - 增强上下文:
· 添加业务字段(如订单号、用户ID)
· 重写getMessage()整合关键信息
三、实战:转账业务异常处理
public class TransferService {
public void transfer(String fromAcc, String toAcc, BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvalidAmountException("转账金额必须大于零", amount); // 金额非法
}
BigDecimal balance = getBalance(fromAcc);
if (balance.compareTo(amount) < 0) {
throw new InsufficientBalanceException(fromAcc, amount); // 精准抛出业务异常
}
// 执行转账逻辑...
}
}
// 异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(InsufficientBalanceException.class)
public ResponseEntity<ErrorResponse> handleBalanceError(InsufficientBalanceException ex) {
ErrorResponse error = new ErrorResponse("BALANCE_ERROR", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
}
四、对比:自定义 vs 内置异常
维度 内置异常 自定义异常
语义明确度 低(如IllegalState) 高(如PaymentFailedException)
错误信息丰富度 基础描述 携带业务ID、操作上下文
系统可维护性 难追溯具体故障点 快速定位问题模块
客户端响应 模糊HTTP状态码 精准错误码与修复建议
关键设计原则:在Service层定义业务异常,Controller层捕获并转换为API错误模型。避免在基础设施层(如DAO)使用自定义业务异常。
结语
自定义异常非语法糖,而是架构设计的关键组件。通过精准定义InvalidOrderStatusException、DuplicateUsernameException等业务异常,配合Spring的全局处理机制,可使系统错误处理路径清晰如业务逻辑本身,显著降低维护成本。

被折叠的 条评论
为什么被折叠?



