总结与最佳实践优秀的异常处理不是简单地捕获和处理异常而是构建一套完整的异常管理策略。以下是一些关键的最佳实践区分业务与系统异常业务异常表示业务逻辑问题系统异常表示技术问题。在业务层抛出业务异常在系统层抛出系统异常。保持异常上下文完整在抛出自定义异常时携带原始异常和关键业务参数确保能够追踪到根本原因。确保资源正确释放使用try-with-resources或finally块确保资源正确关闭避免资源泄漏。避免空catch块永远不要编写空的catch块至少记录异常信息防止问题被掩盖。异常不要用于流程控制异常处理的效率远低于条件分支不应将异常作为常规流程控制的手段。在表现层全局处理异常使用ControllerAdvice实现全局异常处理将异常转换为用户可理解的响应。在业务层精准表达异常通过自定义异常表达业务逻辑错误提供错误码和业务上下文。在持久层封装技术异常捕获具体的数据库异常转换为业务异常避免向上层暴露技术细节。使用错误码设计规范错误码应包含来源信息和唯一编号便于快速定位和解决问题。在分布式系统中使用熔断和重试防止服务雪崩处理暂时性错误确保系统整体可用性。最后记住优秀的异常处理不仅仅是捕获和处理异常而是构建一个完整的异常管理生态系统从异常的定义、抛出、捕获到最终的处理和记录每个环节都需要精心设计和实现。通过遵循本文的最佳实践你可以显著提升系统的健壮性和可维护性减少故障发生加速问题排查。高级异常处理技巧1. AOP统一异常处理使用AOP面向切面编程可以实现更统一的异常处理避免在每个方法中重复编写异常处理代码。// ✅ 正确示例使用AOP统一处理业务异常 Aspect Component public class BusinessExceptionAdvisor { Pointcut(annotation.org.springframework.web.bind.annotation屉情) public void controllerPointcut() { } AfterThrowing(pointcut controllerPointcut(), throwing e) public void handleBusinessException(BusinessException e, JoinPoint joinPoint) { // 获取方法名和参数 String method joinPoint.getSignature().getName(); Object[] args joinPoint.getArgs(); // 构建错误日志 String logMessage String.format(方法 %s 处理异常参数: %s, 错误码: %s, 消息: %s, method, Arrays.toString(args), e.getErrorCode(), e.getMessage()); // 记录错误日志 switch (e.getErrorCode().substring(0, 1)) { case A: // 用户错误 logger.warn(logMessage); break; case B: // 系统业务错误 logger.error(logMessage); break; case C: // 第三方服务错误 logger.error(logMessage); break; } } }AOP异常处理的优势集中管理异常所有异常处理逻辑集中在一个地方便于维护。非侵入式不需要在每个方法中编写异常处理代码。可扩展性可以轻松添加新的异常处理逻辑。使用AOP处理异常的注意事项避免过度捕获只捕获需要处理的异常类型。保留原始异常信息在处理异常时保留原始异常信息。考虑性能影响AOP可能会带来一定的性能开销。2. 分布式系统中的异常处理在微服务架构中异常处理面临着新的挑战。服务间通信可能失败网络波动可能导致请求超时这些都需要特殊的处理策略。服务间通信异常处理使用熔断器模式如Resilience4j或Hystrix防止服务雪崩。实现重试机制对于暂时性错误可以配置适当的重试策略。设计降级策略当服务不可用时提供备用方案或默认值。// ✅ 正确示例使用Resilience4j处理服务间调用异常 CircuitBreaker(name paymentService, fallbackMethod fallbackProcessPayment) Retry(name paymentService, fallbackMethod fallbackProcessPayment) public PaymentResult processPayment(String userId, double amount) { // 调用第三方支付服务 return paymentClient.processPayment(userId, amount); } // 熔断和重试的降级方法 public PaymentResult fallbackProcessPayment(String userId, double amount, Throwable t) { if (t instanceof CircuitBreakerOpenException) { // 熔断触发时的处理 logger.warn(支付服务熔断用户ID: {}, userId); return new PaymentResult(userId, amount, 服务不可用请稍后重试); } else if (t instanceof RetryException) { // 重试失败时的处理 logger.warn(支付服务重试失败用户ID: {}, userId); return new PaymentResult(userId, amount, 服务繁忙请稍后重试); } else { // 其他异常的处理 logger.error(支付服务处理失败用户ID: {}, userId, t); throw new PaymentServiceException(C7001, 支付服务处理失败, t); } }微服务异常处理的最佳实践使用熔断器防止级联故障当某个服务持续失败时熔断器可以阻止对它的进一步调用。实现重试机制处理暂时性错误对于网络波动等暂时性错误可以配置适当的重试策略。设计合理的降级策略当服务不可用时提供备用方案或默认值确保系统整体可用性。使用分布式追踪如Spring Cloud Sleuth和Zipkin跟踪跨服务调用的异常传播。3. 异常与日志的结合优秀的异常处理应该与日志记录紧密结合以便快速定位和解决问题。// ✅ 正确示例在异常处理中记录日志 public class FileService { Autowired private Logger logger; public void processFile(String fileName) { try (FileInputStream fis new FileInputStream(fileName); BufferedReader br new BufferedReader(new InputStreamReader(fis))) { // 处理文件 } catch (IOException e) { // 记录异常信息包含上下文 String logMessage String.format(处理文件 %s 失败, fileName); logger.error(logMessage, e); // 抛出自定义异常 throw new FileProcessingException(B8001, logMessage, e); } } }异常与日志结合的最佳实践在每个异常处理块中记录日志至少记录异常的基本信息和堆栈跟踪。使用合适的日志级别根据异常的严重程度选择合适的日志级别如ERROR、WARN。在日志中包含上下文信息如用户ID、订单ID等帮助快速定位问题。使用MDCMapped Diagnostic Context在分布式系统中使用MDC传递traceId等上下文信息。资源管理与异常处理1. try-with-resources资源管理Java 7引入了try-with-resources语句用于自动管理实现了AutoCloseable接口的资源。这是避免资源泄漏的最佳实践。// ✅ 正确示例try-with-resources自动管理资源 public void processFile() { try (FileInputStream fis new FileInputStream(order.txt); BufferedReader br new BufferedReader(new InputStreamReader(fis))) { String line; while ((line br.readLine()) ! null) { // 处理文件行 } } catch (IOException e) { // 处理IO异常 throw new FileProcessingException(B5001, 文件处理失败, e); } }try-with-resources的工作原理在try块中声明的资源必须实现AutoCloseable接口。当try块执行完毕或发生异常时资源的close方法会被自动调用。无需显式调用close方法避免了资源泄漏风险。传统finally与try-with-resources对比// ❌ 传统finally方式容易出错 public void processFileTraditional() { FileInputStream fis null; try { fis new FileInputStream(order.txt); // 处理文件 } catch (IOException e) { // 处理异常 throw new FileProcessingException(B5002, 文件处理失败, e); } finally { // 资源释放 if (fis ! null) { try { fis.close(); } catch (IOException e) { // 这里可能会忽略异常 } } } } // ✅ try-with-resources方式更简洁安全 public void processFileWithResources() { try (FileInputStream fis new FileInputStream(order.txt); BufferedReader br new BufferedReader(new InputStreamReader(fis))) { // 处理文件 } catch (IOException e) { // 处理异常 throw new FileProcessingException(B5003, 文件处理失败, e); } }资源管理最佳实践优先使用try-with-resources对于实现了AutoCloseable接口的资源优先使用try-with-resources。避免在finally中抛出异常在finally块中抛出异常会掩盖try块中抛出的异常。确保资源正确释放无论是否发生异常资源都应被正确释放。2. 多层资源管理在复杂场景中可能会有多个需要管理的资源。这时需要确保所有资源都被正确关闭。// ✅ 正确示例多层资源管理 public void processMultipleResources() { Connection connection null; PreparedStatement statement null; ResultSet rs null; try { connection dataSource.getConnection(); statement connection.prepareStatement(SELECT * FROM orders); rs statement.executeQuery(); // 处理结果集 } catch (SQLException e) { // 处理数据库异常 throw new DatabaseException(B6001, 数据库操作失败, e); } finally { // 以相反的顺序关闭资源 if (rs ! null) { try { rs.close(); } catch (Exception e) { /* 忽略异常 */ } } if (statement ! null) { try { statement.close(); } catch (Exception e) { /* 忽略异常 */ } } if (connection ! null) { try { connection.close(); } catch (Exception e) { /* 忽略异常 */ } } } }多层资源管理的最佳实践按创建的逆序关闭资源先创建的资源后关闭。在finally块中关闭资源确保无论是否发生异常资源都能被关闭。避免在finally块中抛出异常在finally块中关闭资源时如果发生异常应忽略或记录而不是抛出。