急诊室的故事:当Controller层突然抛出一个异常,Spring MVC就像配备智能分诊系统的三甲医院,@ControllerAdvice是候诊大厅的分诊台,HandlerExceptionResolver们是各科专家,而你的异常就是需要救治的病患。今天我们一起拆解Spring异常处理的分级诊疗系统!
// 核心处理器优先级链(源码节选)
public class DispatcherServlet {
private List<HandlerExceptionResolver> handlerExceptionResolvers;
// 初始化顺序决定了处理优先级
initHandlerExceptionResolvers() {
// 1. ExceptionHandlerExceptionResolver (@ExceptionHandler)
// 2. ResponseStatusExceptionResolver (@ResponseStatus)
// 3. DefaultHandlerExceptionResolver (Spring标准异常转换)
// 4. SimpleMappingExceptionResolver (xml配置异常映射)
}
}
会诊流程图解:
患者异常 → 专科医生1(最高优先级)→ 治不好 → 转诊专科医生2 → … → 急诊大厅(SimpleMappingExceptionResolver)
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver {
@Override
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
// 判断是否有@ExceptionHandler方法可处理该异常
return findExceptionHandlerMethod(handler, exception) != null;
}
}
public class ResponseStatusExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(...) {
// 检查是否有@ResponseStatus注解
ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(
exception.getClass(), ResponseStatus.class);
if (status != null) {
return handleResponseStatus(status, request, response, handler, exception);
}
return null; // 无法处理,转交下一科室
}
}
@ControllerAdvice(basePackages = "com.example.controller")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionTriage {
// 处理所有SQL异常
@ExceptionHandler(SQLException.class)
public ResponseEntity<ErrorResult> handleSQLException(SQLException ex) {
return ResponseEntity.status(503)
.body(new ErrorResult("DATABASE_ERROR", ex.getMessage()));
}
// 拦截所有未捕获的异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResult> fallbackHandler(Exception ex) {
return ResponseEntity.status(500)
.body(new ErrorResult("SYSTEM_ERROR", "系统开小差啦~"));
}
}
匹配模式增强:
@ControllerAdvice(
annotations = RestController.class, // 拦截带有特定注解的Controller
assignableTypes = {BaseController.class}, // 拦截指定类及其子类
basePackageClasses = UserOperation.class // 按功能模块划分
)
public class CustomizedExceptionAdvice {
// 精确到毫厘的异常拦截
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResult> handleValidationException(
MethodArgumentNotValidException ex) {
List<FieldError> errors = ex.getBindingResult().getFieldErrors();
Map<String, String> errorMap = errors.stream()
.collect(Collectors.toMap(
FieldError::getField,
FieldError::getDefaultMessage
));
return ResponseEntity.badRequest()
.body(new ErrorResult("VALIDATION_FAILED", errorMap));
}
public class BusinessException extends RuntimeException {
private final ErrorCode code;
public BusinessException(ErrorCode code, String message) {
super(message);
this.code = code;
}
// getters...
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResult> handleBusinessException(
BusinessException ex) {
return ResponseEntity.status(ex.getCode().getHttpStatus())
.body(new ErrorResult(ex.getCode().name(), ex.getMessage()));
}
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
if (ex instanceof OAuth2AuthenticationException) {
response.setStatus(401);
writeJsonResponse(response, new ErrorResult("AUTH_FAIL", "请重新登录"));
return new ModelAndView(); // 已处理
}
return null; // 转交其他处理器
}
}
// 注册自定义解析器
@Configuration
public class ExceptionConfig implements WebMvcConfigurer {
@Override
public void configureHandlerExceptionResolvers(
List<HandlerExceptionResolver> resolvers) {
resolvers.add(0, new CustomExceptionResolver()); // 最高优先级
}
}
症状表现 | 根本原因 | 诊疗方案 |
---|---|---|
异常被多个处理器重复处理 | 未明确指定异常类型范围 | 精确匹配异常类型树 |
全局处理器不生效 | 包扫描路径未正确配置 | 检查@ControllerAdvice的basePackages |
响应状态码始终200 | 未正确设置response状态 | 使用ResponseEntity或直接设置response |
异步请求异常无法捕获 | 未配置异步异常处理器 | 添加AsyncUncaughtExceptionHandler |
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResult> captureException(Exception ex) {
// 发送异常到监控平台
monitorClient.send(new ErrorEvent(ex));
return ResponseEntity.internalServerError()
.body(new ErrorResult("ERROR", "系统繁忙"));
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResult> handleI18nException(
BusinessException ex, Locale locale) {
String message = messageSource.getMessage(
ex.getCode().name(), null, locale);
return ResponseEntity.status(ex.getCode().getHttpStatus())
.body(new ErrorResult(ex.getCode().name(), message));
}
掌握Spring MVC异常处理体系后,你的应用将拥有三甲医院级别的容错能力。记住:好的异常处理不是消灭异常,而是让系统在遭遇异常时依然能优雅地提供服务。现在打开你的IDE,打造一个永不宕机的"医疗系统"吧!