题目
Spring MVC中如何实现全局异常处理?
信息
- 类型:问答
- 难度:⭐⭐
考点
异常处理机制,@ControllerAdvice注解,@ExceptionHandler注解,最佳实践
快速回答
在Spring MVC中实现全局异常处理的核心步骤:
- 创建带有
@ControllerAdvice注解的全局异常处理器类 - 在方法上使用
@ExceptionHandler注解指定处理的异常类型 - 在方法中定义异常处理逻辑和返回响应(如ModelAndView、ResponseEntity等)
- 结合
@ResponseStatus注解自定义HTTP状态码
一、原理说明
Spring MVC通过DispatcherServlet统一处理请求,当控制器抛出异常时:
- 查找当前控制器内的
@ExceptionHandler方法 - 若未找到,查找
@ControllerAdvice标注的全局异常处理器 - 调用匹配的异常处理方法生成响应
@ControllerAdvice作为组件扫描的全局拦截器,可结合basePackages/annotations限定作用范围。
二、代码示例
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理特定异常
@ExceptionHandler(CustomBusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleBusinessException(CustomBusinessException ex) {
ErrorResponse error = new ErrorResponse(
"BUSINESS_ERROR",
ex.getMessage(),
Instant.now()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
// 处理所有未捕获异常
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
ErrorResponse error = new ErrorResponse(
"SERVER_ERROR",
"Internal server error",
Instant.now()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 错误响应DTO
public class ErrorResponse {
private String code;
private String message;
private Instant timestamp;
// 构造方法/getter/setter省略
}三、最佳实践
- 分层处理:业务异常(如
CustomBusinessException)和系统异常分开处理 - 统一响应格式:所有异常返回标准化JSON结构(包含错误码、消息、时间戳)
- HTTP状态码:通过
@ResponseStatus或ResponseEntity设置符合语义的状态码 - 日志记录:在全局处理器中记录ERROR级别日志
- 避免泄露细节:生产环境中隐藏堆栈信息,防止安全风险
四、常见错误
- 作用域问题:未将
@ControllerAdvice类放入Spring扫描路径 - 异常匹配顺序:
@ExceptionHandler方法按精确度优先匹配,将Exception.class放在最后 - 响应冲突:同时使用
@ResponseBody和ResponseEntity导致重复包装 - 忽略事务回滚:默认仅对RuntimeException回滚,需用
@Transactional(rollbackFor=Exception.class)
五、扩展知识
- HandlerExceptionResolver接口:底层扩展点,可自定义解析器实现更复杂的异常处理逻辑
- ErrorController:处理404等非异常HTTP错误,需实现
ErrorController接口并重写getErrorPath() - Spring Boot增强:通过
ErrorAttributes定制错误属性,或使用@RestControllerAdvice替代@ControllerAdvice(默认含@ResponseBody) - 异步请求处理:异步方法需在
Callable或DeferredResult内抛异常才能被全局处理器捕获