异常处理 (Exceptions)
如果在请求映射过程中发生异常,或者从请求处理器(如 @Controller)中抛出异常,DispatcherServlet 会委托给一个 HandlerExceptionResolver 链来处理异常并提供替代方案(通常是错误响应)。
可用的解析器实现
下表列出了常用的 HandlerExceptionResolver:
| 解析器实现 | 说明 |
|---|---|
ExceptionHandlerExceptionResolver | 最常用。用于调用 @Controller 或 @ControllerAdvice 类中的 @ExceptionHandler 方法。 |
ResponseStatusExceptionResolver | 根据异常上的 @ResponseStatus 注解将其映射到 HTTP 状态码。 |
DefaultHandlerExceptionResolver | 解析 Spring MVC 产生的标准异常(如参数绑定失败),并将其映射到对应的 HTTP 状态码。 |
SimpleMappingExceptionResolver | 允许在异常类名和错误视图名之间建立简单的映射关系(适用于传统 JSP/Thymeleaf 页面跳转)。 |
解析器链 (Chain of Resolvers)
你可以通过在配置中声明多个 HandlerExceptionResolver Bean 并设置其 order 属性来形成一条链。order 值越小,优先级越高。
解析器的处理逻辑通常遵循以下契约:
- 返回一个指向错误视图的
ModelAndView。 - 返回一个空的
ModelAndView(表示异常已处理)。 - 返回
null(表示无法处理,交给链中的下一个解析器)。
容器错误页面 (Container Error Page)
如果所有解析器都无法处理异常,或者响应状态码被显式设为错误(4xx, 5xx),Servlet 容器可以渲染默认的 HTML 错误页面。你可以通过在 web.xml 中配置 <error-page> 来自定义:
xml
<error-page>
<location>/error</location>
</error-page>在这种情况下,容器会向 /error 发起内部请求,由 DispatcherServlet 将其映射到一个控制器进行自定义渲染:
java
@RestController
public class ErrorController {
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
return map;
}
}kotlin
@RestController
class ErrorController {
@RequestMapping(path = ["/error"])
fun handle(request: HttpServletRequest): Map<String, Any> {
val map = HashMap<String, Any>()
map["status"] = request.getAttribute("jakarta.servlet.error.status_code")
map["reason"] = request.getAttribute("jakarta.servlet.error.message")
return map
}
}补充教学
1. 为什么推荐使用 @ControllerAdvice?
早期异常处理散落在各个 Controller 中(使用 @ExceptionHandler)。 现代推荐:创建一个标注了 @ControllerAdvice 的全局异常处理类。
- 解耦:业务代码里只管抛出异常(如
UserNotFoundException),不需要关心渲染。 - 统一:全局异常类负责捕获特定异常,并统一转换成标准的 REST 错误格式(如包含
timestamp和msg的 JSON)。
2. Spring Boot 里的 /error 是怎么来的?
在 Spring Boot 中,你并不需要配置 web.xml 或手动写 ErrorController。
- BasicErrorController:Spring Boot 自动为你配置了一个
BasicErrorController。 - 策略:它会根据请求头的
Accept自动判断。如果是浏览器请求,返回 HTML 错误页面(你可以通过在templates/error/下创建404.html自定义);如果是代码(JSON)请求,返回标准的错误 JSON。
3. 理解 ExceptionResolver 的吞没 (Swallow) 行为
请记住:一旦某个解析器处理了异常并返回了非空 ModelAndView,该异常就被认为“处理完成”了,之后不会再往外抛给 Tomcat 或 Spring Security。这在进行全局审计或异常埋点时需要额外留意。