侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计多层服务调用中的异常处理框架

2025-12-13 / 0 评论 / 4 阅读

题目

设计多层服务调用中的异常处理框架

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

异常封装与转换,异常链维护,资源清理安全,上下文信息传递

快速回答

核心解决方案要点:

  • 创建自定义异常基类,封装错误码和上下文元数据
  • 使用异常链(initCause()/带Throwable参数的构造器)保持原始异常
  • 结合try-with-resourcesfinally块确保资源释放
  • 在服务边界进行异常转换(如DAO→Service→Controller层)
  • 使用线程局部变量(ThreadLocal)传递调用上下文
## 解析

问题场景

在分布式系统中,一个业务请求可能跨越DAO层、服务层、RPC层等多个调用层级。需要设计异常处理框架确保:

  1. 底层异常信息不丢失
  2. 敏感信息(如SQL语句)不暴露给上层
  3. 资源(数据库连接、文件句柄)100%释放
  4. 携带业务上下文(如订单ID、用户ID)

核心实现方案

1. 自定义异常体系

// 基础业务异常(含错误码和上下文)
public abstract class BusinessException extends RuntimeException {
    private final String errorCode;
    private final Map<String, String> context = new HashMap<>();

    public BusinessException(String message, String errorCode, Throwable cause) {
        super(message, cause);  // 关键:保存原始异常
        this.errorCode = errorCode;
    }

    public void addContext(String key, String value) {
        context.put(key, value);
    }

    // 获取完整上下文信息(JSON格式)
    public String getContextInfo() { /*...*/ }
}

// 分层子类示例(DAO层)
public class DaoException extends BusinessException {
    public DaoException(String message, Throwable cause) {
        super(message, "ERR_DAO_001", cause);
    }
}

2. 异常转换与链式传递

// Service层处理DAO异常
public Order getOrder(String orderId) throws ServiceException {
    try (Connection conn = dataSource.getConnection()) {  // try-with-resources
        OrderDAO dao = new OrderDAO(conn);
        return dao.fetchOrder(orderId);

    } catch (DaoException ex) {
        // 添加业务上下文
        ex.addContext("orderId", orderId);

        // 转换为服务层异常(保留原始异常)
        throw new ServiceException("订单查询失败", "ERR_SVC_200", ex);
    } finally {
        // 清理线程局部变量(如事务ID)
        TransactionContext.clear();
    }
}

最佳实践

  • 分层转换:每层封装专属异常(DAOException → ServiceException → ApiException)
  • 上下文传递:使用ThreadLocal存储请求级数据(如MDC
  • 资源释放
    • 优先使用try-with-resources(实现AutoCloseable
    • 复杂资源在finally中显式释放
  • 日志记录:在顶层统一记录异常(包含完整上下文)

常见错误

错误示例后果修正方案
catch(Exception e) { throw new MyException("error"); }原始异常丢失传入原始异常:throw new MyException("error", e)
catch(SQLException e) { throw new ServiceException(e.getMessage()); }堆栈信息截断直接传递异常对象
finally中抛出异常覆盖主逻辑异常使用嵌套try-catch处理清理异常

扩展知识

  • 响应式编程异常处理:Project Reactor中的onErrorResume()/onErrorMap()
  • JVM限制
    • 异常cause链最多支持250层(可配置)
    • 频繁抛出异常影响性能(创建栈快照)
  • 框架集成:Spring的@ControllerAdvice统一异常处理