当前位置:首页 >休闲 >详解SpringBoot接口异常处理机制及源码分析 如果异常在最后仍然存在

详解SpringBoot接口异常处理机制及源码分析 如果异常在最后仍然存在

2024-07-01 11:20:20 [百科] 来源:避面尹邢网

详解SpringBoot接口异常处理机制及源码分析

作者:Springboot实战案例锦集 开发 前端 如果异常仍然未解决,详解析则为null,接口机制及源以便后续的异常解析器尝试,如果异常在最后仍然存在,处理则允许它向上冒泡到Servlet容器。码分

环境:Springboot3.0.5

概述

如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,详解析DispatcherServlet将委托给HandlerExceptionResolver。接口机制及源

详解SpringBoot接口异常处理机制及源码分析 如果异常在最后仍然存在

下表列出了可用的异常HandlerExceptionResolver实现。

详解SpringBoot接口异常处理机制及源码分析 如果异常在最后仍然存在

HandlerExceptionResolver 实现类:

详解SpringBoot接口异常处理机制及源码分析 如果异常在最后仍然存在

HandlerExceptionResolver

描述

SimpleMappingExceptionResolver

异常类名和错误视图名之间的映射。用于在浏览器应用程序中渲染错误页面。码分

DefaultHandlerExceptionResolver

解析Spring MVC引发的异常,并将其映射为HTTP状态码。接口机制及源

ResponseStatusExceptionResolver

使用@ResponseStatus注解解析异常,并根据注解中的处理值将异常映射为HTTP状态码。

ExceptionHandlerExceptionResolver

通过在@Controller或@ControllerAdvice类中调用由@ExceptionHandler注释的方法来解决异常。

我们可以声明多个HandlerExceptionResolver

HandlerExceptionResolver的约定规定它可以返回:

  • 指向错误视图的ModelAndView。
  • 如果异常是在解析器中处理的,则返回空的ModelAndView。
  • 如果异常仍然未解决,则为null,以便后续的解析器尝试,如果异常在最后仍然存在,则允许它向上冒泡到Servlet容器。

Controller接口调用原理

SpringMVC请求入口通过DispatcherServlet执行大致核心流程如下:

  1. 首先通过HandlerMapping确定目标Handler对象(如果接口是Controller那么这里会是 HandlerMethod)
  2. 通过上一步Handler对象,确定执行真正调用的HandlerAdapter

这里以Controller接口为例,HandlerAdapter对象为RequestMappingHandlerAdapter。

DispatcherServlet

public class DispatcherServlet extends FrameworkServlet {   protected void doDispatch(...) throws Exception {     HandlerExecutionChain mappedHandler = null;    try {       Exception dispatchException = null;      // 根据请求确定Handler对象(遍历所有的HandlerMapping)      mappedHandler = getHandler(processedRequest);      // 根据上一步确定的Handler对象,确定HandlerAdapter对象      HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());      // 真正执行目标方法的调用      mv = ha.handle(processedRequest, response, mappedHandler.getHandler());    } catch (Exception ex) {       dispatchException = ex;    } catch (Throwable err) {       dispatchException = new ServletException("Handler dispatch failed: " + err, err);    }    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  }}

RequestMappingHandlerAdapter

public class RequestMappingHandlerAdapter {   protected ModelAndView handleInternal(...) throws Exception {     ModelAndView mav;    mav = invokeHandlerMethod(request, response, handlerMethod);  }  protected ModelAndView invokeHandlerMethod(...) throws Exception {     // ...    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);    // ... 对ServletInvocableHandlerMethod进行配置    invocableMethod.invokeAndHandle(webRequest, mavContainer);    return getModelAndView(mavContainer, modelFactory, webRequest);  }}

ServletInvocableHandlerMethod执行参数解析目标Controller方法调用及返回值的处理。

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {   public void invokeAndHandle(...) throws Exception {     // 该方法中会进行请求参数的解析及目标方法的调用    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);    // ...    try {       // 处理返回值      this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);    } catch (Exception ex) {       throw ex;    }  }}

通过上面的源码分析,在调用过程中如果发生了异常会将异常直接抛出,在DispatcherServlet中会进行异常的处理。

异常解析原理分析

接着上面的源码分析,当发生异常后最终会在DispatcherServlet#processDispatchResult方法中进行处理。

public class DispatcherServlet extends FrameworkServlet {   /*    * 默认情况下有如下2个异常解析器    * 1. DefaultErrorAttributes    * 2. ExceptionHandlerExceptionResolver    */  private List<HandlerExceptionResolver> handlerExceptionResolvers;  private void processDispatchResult(...) {     if (exception != null) {       Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);      // 处理异常      mv = processHandlerException(request, response, handler, exception);    }  }  protected ModelAndView processHandlerException(...) throws Exception {     ModelAndView exMv = null;    if (this.handlerExceptionResolvers != null) {       // 遍历所有的异常解析器      for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {         // 解析异常,核心的解析器是ExceptionHandlerExceptionResolver        exMv = resolver.resolveException(request, response, handler, ex);      }    }    // ...  }}

ExceptionHandlerExceptionResolver类继承自AbstractHandlerMethodExceptionResolver该类又继承自AbstractHandlerExceptionResolver。

// 调用父类(AbstractHandlerExceptionResolver)方法public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {   public ModelAndView resolveException(...) {     // doResolveException该方法在子类AbstractHandlerMethodExceptionResolver中重写    ModelAndView result = doResolveException(request, response, handler, ex);  }}

AbstractHandlerMethodExceptionResolver

public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {   protected final ModelAndView doResolveException(...) {     HandlerMethod handlerMethod = (handler instanceof HandlerMethod hm ? hm : null);    return doResolveHandlerMethodException(request, response, handlerMethod, ex);  }}

ExceptionHandlerExceptionResolver

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver implements ApplicationContextAware, InitializingBean {   protected ModelAndView doResolveHandlerMethodException(...) {     // 该方法中会先从当前的Controller中查找是否有@ExceptionHandler注解的方法(如果匹配)    // 如果没有再从全局的异常处理类句柄中查找    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);    if (exceptionHandlerMethod == null) {       return null;    }    // 执行异常处理方法的调用    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);  }  protected ServletInvocableHandlerMethod getExceptionHandlerMethod(...) {     Class<?> handlerType = null;    if (handlerMethod != null) {         handlerType = handlerMethod.getBeanType();      // 缓存并设置当前执行Class对应的ExceptionHandlerMethodResolver      // ExceptionHandlerMethodResolver构造函数中会解析当前类中的所有方法是否有@ExceptionHandler注解      ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.computeIfAbsent(handlerType, ExceptionHandlerMethodResolver::new);      // 解析是否匹配当前发生的异常      Method method = resolver.resolveMethod(exception);      if (method != null) {         return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);      }    }    // 如果上面的执行的Class中没有找到对应处理器,那么就从全局的异常处理中进行查找匹配    // 这里的exceptionHandlerAdviceCache集合在类初始化执行时已经处理完成    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {       ControllerAdviceBean advice = entry.getKey();      if (advice.isApplicableToBeanType(handlerType)) {         ExceptionHandlerMethodResolver resolver = entry.getValue();        Method method = resolver.resolveMethod(exception);        if (method != null) {           return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);        }      }    }    return null;  }}

通过上面的源码分析你应该知道了关于SpringMVC中异常处理的原理。

当上面的异常处理机制都没法处理,那么将会调用默认的/error接口。

public class ErrorMvcAutoConfiguration {   @Bean  @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)  public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {     return new BasicErrorController(errorAttributes, this.serverProperties.getError(), errorViewResolvers.orderedStream().toList());  }  }

BasicErrorController

@Controller@RequestMapping("${ server.error.path:${ error.path:/error}}")public class BasicErrorController extends AbstractErrorController { }

上面的错误接口/error在容器启动时会自动注册到内嵌的容器中,如:Tomcat。

责任编辑:武晓燕 来源: 实战案例锦集 解析器Servlet容器

(责任编辑:百科)

    推荐文章
    热点阅读