Spring Boot 全局异常捕获机制详解

在 Spring Boot 中,全局异常捕获机制是处理 REST HTTP 请求时的一个重要功能,它可以确保所有未被捕获的异常都能被统一处理。本文将深入探讨 Spring Boot 中全局异常捕获的实现,从请求进入到异常处理的全过程。

请求处理流程概述

  1. 请求进入 DispatcherServlet:所有 HTTP 请求首先到达 DispatcherServlet
  2. 调用处理器方法DispatcherServlet 调用相应的处理器方法(Controller 方法)。
  3. 异常捕获:如果在处理器方法中抛出异常,DispatcherServlet 会调用异常处理方法。
  4. 调用 HandlerExceptionResolver:调用所有注册的 HandlerExceptionResolver 来处理异常。
  5. 默认错误处理:如果所有的 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 中全局异常捕获的整个过程:

  1. 请求进入 DispatcherServlet
  2. 调用处理器方法
  3. 捕获处理器方法抛出的异常
  4. 调用 HandlerExceptionResolver 来处理异常。
  5. 如果异常没有被处理,最终由 BasicErrorController 处理

这种机制确保了在处理请求过程中,所有未被捕获的异常都能被统一处理,提高了应用的健壮性和可维护性。

通过对 DispatcherServletHandlerExceptionResolverBasicErrorController 的源码分析,我们可以深入理解 Spring Boot 的异常处理机制,帮助开发者在实际项目中更好地应用这一机制。

posted on 2024-07-01 14:52  滚动的蛋  阅读(400)  评论(0编辑  收藏  举报

导航