想象我们正在设计一个银行转账系统。转账失败确实是一个非常严重的问题,需要确保每个程序员都正确处理。让我们看看两种可能的设计方案:

方案1:使用受检异常

public class BankService {
    // 使用受检异常强制处理转账失败的情况
    public void transfer(Account from, Account to, BigDecimal amount) 
            throws InsufficientFundsException, AccountLockedException {
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientFundsException("余额不足");
        }
        if (from.isLocked() || to.isLocked()) {
            throw new AccountLockedException("账户被锁定");
        }
        // 执行转账...
    }
}
 
// 调用代码
public void processTransfer() {
    try {
        bankService.transfer(accountA, accountB, new BigDecimal("1000"));
    } catch (InsufficientFundsException e) {
        // 必须处理余额不足
    } catch (AccountLockedException e) {
        // 必须处理账户锁定
    }
}

方案2:使用运行时异常(更常见的做法)

public class BankService {
    // 使用运行时异常处理业务异常
    public void transfer(Account from, Account to, BigDecimal amount) {
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientFundsException("余额不足");
        }
        if (from.isLocked() || to.isLocked()) {
            throw new AccountLockedException("账户被锁定");
        }
        // 执行转账...
    }
}
 
// 在合适的层级统一处理异常
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(InsufficientFundsException.class)
    public ResponseEntity<ApiResponse> handleInsufficientFunds(InsufficientFundsException e) {
        // 统一处理余额不足异常
        return ResponseEntity.badRequest().body(
            new ApiResponse("INSUFFICIENT_FUNDS", "余额不足,请检查账户余额")
        );
    }
}

为什么第二种方案更好?这里有几个重要的原因:

  1. 异常传播的灵活性
    • 运行时异常可以自由传播到最适合处理它的层级
    • 受检异常会强制每个中间层都声明throws或try-catch,导致代码污染
  2. 关注点分离
    • 业务逻辑应该关注正常的业务流程
    • 异常处理最好在统一的地方处理,而不是散布在各处
  3. 代码可维护性
// 使用受检异常时,添加新的异常类型需要修改所有调用链上的方法签名
public void transfer() throws InsufficientFundsException, AccountLockedException,
                            NewException1, NewException2 {  // 需要不断添加新异常
    // 转账逻辑
}
 
// 使用运行时异常时,只需在异常处理器中添加新的处理方法
@ExceptionHandler(NewBusinessException.class)
public ResponseEntity<?> handleNewException(NewBusinessException e) {
    // 处理新的异常类型
}