掌握 Spring @ExceptionHandler,让异常处理不再头疼
/ 标准化错误响应体@Data// 业务错误码// 用户友好提示@ResponseStatus(code = HttpStatus.CONFLICT, reason = "数据冲突")// 空方法体,仅通过注解配置初级阶段:Controller内使用@ExceptionHandler中级阶段:@ControllerAdvice全局处理高级阶段自定义异常体系错误码标准化异常转换与包装4. 专家阶段异
·
一、异常处理的烦恼:为什么需要@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);
}
九、性能优化小贴士
- 异常分类处理:不同异常走不同逻辑
- 避免过度包装:不要多层嵌套异常
- 日志记录:在全局处理器中添加日志
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleException(Exception ex) {
log.error("系统异常", ex); // 重要!
// ...其他处理
}
🎯 总结:异常处理升级路线图
- 初级阶段:Controller内使用@ExceptionHandler
- 中级阶段:@ControllerAdvice全局处理
- 高级阶段:
- 自定义异常体系
- 错误码标准化
- 异常转换与包装
4. 专家阶段:
- 异常监控报警
- 自动化异常文档
- 智能异常分类
关键收获:
好的异常处理应该像城市的排水系统——平时看不见,暴雨时不内涝!使用@ExceptionHandler让你的代码既整洁又健壮。
更多推荐




所有评论(0)