spring mvc全局异常处理核心是handlerexceptionresolver机制,仅拦截controller层未捕获异常;方式一实现接口最底层可控,方式二@controlleradvice+@exceptionhandler推荐使用,方式三simplemappingexceptionresolver已过时。

Spring MVC 处理全局未捕获的系统异常,核心是通过 异常解析器(HandlerExceptionResolver)机制 统一拦截 Controller 层抛出、未被显式 catch 的运行时异常(如 NullPointerException、ArithmeticException、自定义业务异常等)。它不处理 Filter 或 Interceptor 中抛出的异常,也不捕获 Spring 容器启动阶段或 Servlet 初始化阶段的异常,只作用于 DispatcherServlet 分发请求后、Handler(即 Controller 方法)执行过程中抛出的异常。
以下三种主流方式中,前两种是真正意义上的“全局异常解析器”实现,第三种虽常用,但本质属于 AOP 增强,并非严格意义上的 HandlerExceptionResolver 实例:
✅ 方式一:实现 HandlerExceptionResolver 接口(最底层、最可控)
这是 Spring 提供的原始扩展点,所有异常最终都经由 DispatcherServlet 调用已注册的 HandlerExceptionResolver 实例来处理。
- 自定义类实现接口,并标注
@Component让其被 Spring 扫描注册; -
resolveException()方法接收HttpServletRequest、HttpServletResponse、handler(出错的 Controller 方法)、Exception四个参数; - 可根据异常类型、请求头(如是否为 AJAX)、HTTP 状态码等做精细化响应;
- 返回
ModelAndView(跳转页面)或直接写响应体(如 JSON),需手动设置状态码和响应内容。
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// 1. 判断是否为 REST 请求(例如含 "application/json" Accept 头)
boolean isJsonRequest = request.getHeader("Accept") != null &&
request.getHeader("Accept").contains("application/json");
// 2. 根据异常类型构造响应
int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
String message = "系统繁忙,请稍后再试";
if (ex instanceof IllegalArgumentException) {
status = HttpStatus.BAD_REQUEST.value();
message = "参数错误:" + ex.getMessage();
} else if (ex instanceof ArithmeticException) {
status = HttpStatus.BAD_REQUEST.value();
message = "计算异常:除零操作";
}
// 3. 统一返回 JSON(适用于前后端分离场景)
if (isJsonRequest) {
try {
response.setStatus(status);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(
new ObjectMapper().writeValueAsString(Map.of("code", status, "msg", message))
);
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView(); // 空视图,避免后续渲染
}
// 4. 普通页面请求,转发到 error.jsp
ModelAndView mv = new ModelAndView("error");
mv.addObject("errorMessage", message);
mv.setStatus(HttpStatus.valueOf(status));
return mv;
}
}
⚠️ 注意:若同时存在多个
HandlerExceptionResolver,Spring 会按注册顺序尝试,直到某个 resolver 返回非 null 的ModelAndView。建议只保留一个主解析器,避免冲突。
✅ 方式二:使用 @ControllerAdvice + @ExceptionHandler(推荐、简洁、语义清晰)
这不是独立的 HandlerExceptionResolver 实现类,但 Spring 内部会将其自动包装为 ExceptionHandlerExceptionResolver(继承自 HandlerExceptionResolver),因此它本质仍是异常解析器机制的一部分,且优先级高于自定义 HandlerExceptionResolver。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
-
@ControllerAdvice标注的类会被 Spring 视为全局增强器,对所有@Controller/@RestController生效; -
@ExceptionHandler方法可按异常类型精准匹配,支持返回String(视图名)、ResponseEntity、@ResponseBody对象等; - 支持方法参数自动注入(如
HttpServletRequest、HttpServletResponse、@RequestBody对象等); - 可配合
@ResponseStatus直接设定 HTTP 状态码。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<result> handleNPE(NullPointerException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Result.fail("空值异常:" + e.getMessage()));
}
@ExceptionHandler(Exception.class) // 兜底
public ResponseEntity<result> handleAll(Exception e) {
e.printStackTrace(); // 生产环境建议用日志框架替代
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Result.fail("系统异常,请联系管理员"));
}
}</result></result>
✅ 优势:代码集中、类型安全、支持细粒度响应;无需手动写 JSON 序列化或设响应头;天然兼容 REST 场景。
❌ 方式三:SimpleMappingExceptionResolver(已过时,仅作兼容了解)
这是一个 XML 配置型 Bean,Spring 5.0+ 已不推荐,Boot 2.x+ 默认不再启用。它只能做简单映射(异常类 → 视图名),无法动态生成响应内容、无法处理 JSON、无法记录日志或调用服务。
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="defaultErrorView" value="error"></property><property name="exceptionAttribute" value="ex"></property><property name="exceptionMappings"><props><prop key="java.lang.ArithmeticException">arith_error</prop><prop key="com.example.ParamsException">param_error</prop></props></property></bean>
⚠️ 缺陷:无法返回 JSON、无法定制 HTTP 状态码、无法注入上下文对象、配置僵硬。新项目应避免使用。
统一异常处理的关键前提:
- 异常必须从 Controller 方法向上抛出(不能在 Controller 内部
try-catch吞掉); - 自定义异常建议继承
RuntimeException,避免强制声明 throws; -
@ControllerAdvice类需确保被 Spring 扫描到(包路径在 component-scan 范围内); - 若使用
HandlerExceptionResolver,注意它对@ResponseBody和@RestController的 JSON 响应支持需手动处理输出流; - 日志记录建议统一在异常处理器中完成,避免每处
e.printStackTrace()。
不复杂但容易忽略的是:异常是否真的到达了 DispatcherServlet 的异常处理链路——比如 Filter 抛异常、异步线程中抛异常、定时任务中抛异常,这些都不会被上述任何一种方式捕获。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










