Spring Boot 全局异常捕获机制详解
在 Spring Boot 中,全局异常捕获机制是处理 REST HTTP 请求时的一个重要功能,它可以确保所有未被捕获的异常都能被统一处理。本文将深入探讨 Spring Boot 中全局异常捕获的实现,从请求进入到异常处理的全过程。
请求处理流程概述
- 请求进入
DispatcherServlet
:所有 HTTP 请求首先到达DispatcherServlet
。 - 调用处理器方法:
DispatcherServlet
调用相应的处理器方法(Controller 方法)。 - 异常捕获:如果在处理器方法中抛出异常,
DispatcherServlet
会调用异常处理方法。 - 调用
HandlerExceptionResolver
:调用所有注册的HandlerExceptionResolver
来处理异常。 - 默认错误处理:如果所有的
HandlerExceptionResolver
都没有处理该异常,最终会由BasicErrorController
来处理。
1. 请求进入 DispatcherServlet
在 Spring MVC 中,DispatcherServlet
是核心组件,负责接收请求并将其分发到相应的处理器。在 DispatcherServlet
中,doService
方法是处理请求的起点:
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
doDispatch(request, response);
} catch (Exception ex) {
processHandlerException(request, response, null, ex);
}
}
}
2. 请求分发与异常捕获
doService
方法调用了 doDispatch
方法,doDispatch
是请求分发的核心方法,它会调用具体的处理器方法。如果在处理过程中抛出异常,doDispatch
会捕获该异常:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 调用处理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
3. 处理异常结果
processDispatchResult
方法根据处理结果和异常状态决定接下来的操作:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
mv = processHandlerException(request, response, mappedHandler.getHandler(), exception);
errorView = (mv != null);
}
}
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
} else {
if (errorView) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
4. 调用 HandlerExceptionResolver
processHandlerException
方法会遍历所有的 HandlerExceptionResolver
来处理异常:
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
ModelAndView exMv = null;
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
return exMv;
} else {
throw ex;
}
}
5. ExceptionHandlerExceptionResolver
处理自定义异常
ExceptionHandlerExceptionResolver
是处理通过 @ExceptionHandler
注解定义的异常处理方法的核心类:
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver {
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod, Exception exception) {
InvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod != null) {
try {
return exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
} catch (Exception ex) {
logger.error("Failed to invoke @ExceptionHandler method", ex);
}
}
return null;
}
}
6. 默认错误处理 BasicErrorController
如果所有的 HandlerExceptionResolver
都没有处理该异常,最终会由 BasicErrorController
来处理:
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController implements ErrorController {
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
try {
return HttpStatus.valueOf(statusCode);
} catch (Exception ex) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}
ENd
通过以上分析,我们可以清晰地看到 Spring Boot 中全局异常捕获的整个过程:
- 请求进入
DispatcherServlet
。 - 调用处理器方法。
- 捕获处理器方法抛出的异常。
- 调用
HandlerExceptionResolver
来处理异常。 - 如果异常没有被处理,最终由
BasicErrorController
处理。
这种机制确保了在处理请求过程中,所有未被捕获的异常都能被统一处理,提高了应用的健壮性和可维护性。
通过对 DispatcherServlet
、HandlerExceptionResolver
和 BasicErrorController
的源码分析,我们可以深入理解 Spring Boot 的异常处理机制,帮助开发者在实际项目中更好地应用这一机制。