一、异常处理的烦恼:为什么需要@ExceptionHandler?

想象你在餐厅点餐🍽️:

  • 正常情况:点菜 → 厨师做菜 → 上菜
  • 异常情况:点了个不存在的菜 → 服务员怎么处理?


二、@ExceptionHandler 核心原理 🧠

2.1 传统 vs 声明式异常处理

方式 代码量 可维护性 响应一致性 关注点分离
try-catch 难保证 混合
@ExceptionHandler 容易统一 分离

2.2 处理流程解析


三、基础使用三步走 🚶♂️

3.1 第一步:创建异常处理器

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(OrderNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleOrderNotFound(OrderNotFoundException ex) {
        return ResponseEntity
                .status(HttpStatus.NOT_FOUND)
                .body(new ErrorResponse("ORDER_NOT_FOUND", ex.getMessage()));
    }
}

3.2 第二步:定义统一错误格式

// 标准化错误响应体
@Data
@AllArgsConstructor
public class ErrorResponse {
    private String code;  // 业务错误码
    private String message; // 用户友好提示
    private long timestamp = System.currentTimeMillis();
}

3.3 第三步:抛出业务异常

@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
    return orderRepository.findById(id)
            .orElseThrow(() -> new OrderNotFoundException("Order not found: " + id));
}

四、5种高级用法 🔥

4.1 处理多个异常类

@ExceptionHandler({
    PaymentFailedException.class,
    InventoryShortageException.class
})
public ResponseEntity handleBusinessExceptions(RuntimeException ex) {
    // 统一处理支付/库存异常
}

4.2 获取请求上下文

@ExceptionHandler(ValidationException.class)
public ResponseEntity handleValidationError(
        ValidationException ex,
        WebRequest request) {
    
    String requestId = request.getHeader("X-Request-ID");
    // 使用请求上下文信息
}

4.3 自定义状态码

@ResponseStatus(code = HttpStatus.CONFLICT, reason = "数据冲突")
@ExceptionHandler(DataConflictException.class)
public void handleConflict() {
    // 空方法体,仅通过注解配置
}

4.4 异常处理优先级

4.5 异常转换

@ExceptionHandler(SQLException.class)
public ResponseEntity handleDatabaseError(SQLException ex) {
    throw new ServiceUnavailableException("Database error", ex);
}

六、对比:各种异常处理方式

方式 优点 缺点 适用场景
方法内try-catch 直观简单 代码重复 简单小项目
@ExceptionHandler 集中处理 仅限当前控制器 控制器特定异常
@ControllerAdvice 全局统一 配置稍复杂 企业级应用
HandlerExceptionResolver 完全控制 实现复杂 特殊需求

七、生产环境最佳实践 🏆

7.1 异常分类策略

异常类型 HTTP状态码 日志级别 是否报警
参数校验异常 400 Bad Request WARN
认证失败异常 401 Unauthorized INFO
权限不足异常 403 Forbidden WARN
业务逻辑异常 409 Conflict INFO
系统内部异常 500 Internal Server Error ERROR

7.2 日志记录技巧

@ExceptionHandler(Exception.class)
public ResponseEntity handleUnexpectedError(
        Exception ex, 
        HttpServletRequest request) {
    
    log.error("Unexpected error processing {} {}", 
        request.getMethod(), 
        request.getRequestURI(),
        ex);
    
    return ResponseEntity.internalServerError().build();
}

7.3 测试方案

@SpringBootTest
class ExceptionHandlerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldReturn404WhenOrderNotFound() throws Exception {
        mockMvc.perform(get("/orders/999"))
               .andExpect(status().isNotFound())
               .andExpect(jsonPath("$.code").value("ORDER_NOT_FOUND"));
    }
}

八、常见问题解答 ❓

Q1: 如何覆盖Spring默认的/error处理?

@RestControllerAdvice
public class CustomErrorController implements ErrorController {
    
    @RequestMapping("/error")
    public ResponseEntity handleDefaultError(HttpServletRequest request) {
        // 自定义处理
    }
}

Q2: 处理器方法的返回值有哪些选择?

  • ResponseEntity - 完全控制HTTP响应
  • @ResponseBody 对象 - 自动序列化为JSON/XML
  • ModelAndView - 返回错误页面(传统MVC)
  • String - 直接返回错误消息

Q3: 如何实现国际化错误消息?

@ExceptionHandler
public ResponseEntity handleI18nError(
        Exception ex,
        Locale locale) {
    
    String message = messageSource.getMessage(
        "error.order.not_found", 
        null, 
        locale);
    
    return ResponseEntity.badRequest().body(message);
}

九、性能优化小贴士

  1. 异常分类处理:不同异常走不同逻辑
  2. 避免过度包装:不要多层嵌套异常
  3. 日志记录:在全局处理器中添加日志
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleException(Exception ex) {
    log.error("系统异常", ex);  // 重要!
    // ...其他处理
}

🎯 总结:异常处理升级路线图

  1. 初级阶段:Controller内使用@ExceptionHandler
  2. 中级阶段:@ControllerAdvice全局处理
  3. 高级阶段
  • 自定义异常体系   
  • 错误码标准化   
  • 异常转换与包装

     4. 专家阶段

  • 异常监控报警
  • 自动化异常文档
  • 智能异常分类

关键收获

好的异常处理应该像城市的排水系统——平时看不见,暴雨时不内涝!使用@ExceptionHandler让你的代码既整洁又健壮。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐