国产在线精品一区二区三区直播_精品国产乱码久久久久久影片_欧美老妇人与禽交_男人天堂2018手机在线版_亚洲中文精品久久久久久不卡_国产毛多水多做爰爽爽爽_野外做受三级视频_中文天堂在线www_国产精品无码久久四虎_国产在线观看免费人成视频

您的位置:首頁 >熱點 >

詳解SpringBoot接口異常處理機制及源碼分析_今日精選

環(huán)境:Springboot3.0.5

概述

如果在請求映射期間發(fā)生異常或從請求處理程序(例如@Controller)拋出異常,DispatcherServlet將委托給HandlerExceptionResolver


(資料圖片僅供參考)

下表列出了可用的HandlerExceptionResolver實現(xiàn)。

HandlerExceptionResolver實現(xiàn)類:

HandlerExceptionResolver

描述

SimpleMappingExceptionResolver

異常類名和錯誤視圖名之間的映射。用于在瀏覽器應用程序中渲染錯誤頁面。

DefaultHandlerExceptionResolver

解析Spring MVC引發(fā)的異常,并將其映射為HTTP狀態(tài)碼。

ResponseStatusExceptionResolver

使用@ResponseStatus注解解析異常,并根據(jù)注解中的值將異常映射為HTTP狀態(tài)碼。

ExceptionHandlerExceptionResolver

通過在@Controller或@ControllerAdvice類中調(diào)用由@ExceptionHandler注釋的方法來解決異常。

我們可以聲明多個HandlerExceptionResolver

HandlerExceptionResolver的約定規(guī)定它可以返回:

指向錯誤視圖的ModelAndView。如果異常是在解析器中處理的,則返回空的ModelAndView。如果異常仍然未解決,則為null,以便后續(xù)的解析器嘗試,如果異常在最后仍然存在,則允許它向上冒泡到Servlet容器。Controller接口調(diào)用原理

SpringMVC請求入口通過DispatcherServlet執(zhí)行大致核心流程如下:

首先通過HandlerMapping確定目標Handler對象(如果接口是Controller那么這里會是HandlerMethod)通過上一步Handler對象,確定執(zhí)行真正調(diào)用的HandlerAdapter

這里以Controller接口為例,HandlerAdapter對象為RequestMappingHandlerAdapter

DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {  protected void doDispatch(...) throws Exception {    HandlerExecutionChain mappedHandler = null;    try {      Exception dispatchException = null;      // 根據(jù)請求確定Handler對象(遍歷所有的HandlerMapping)      mappedHandler = getHandler(processedRequest);      // 根據(jù)上一步確定的Handler對象,確定HandlerAdapter對象      HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());      // 真正執(zhí)行目標方法的調(diào)用      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執(zhí)行參數(shù)解析目標Controller方法調(diào)用及返回值的處理

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {  public void invokeAndHandle(...) throws Exception {    // 該方法中會進行請求參數(shù)的解析及目標方法的調(diào)用    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);    // ...    try {      // 處理返回值      this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);    } catch (Exception ex) {      throw ex;    }  }}

通過上面的源碼分析,在調(diào)用過程中如果發(fā)生了異常會將異常直接拋出,在DispatcherServlet中會進行異常的處理。

異常解析原理分析

接著上面的源碼分析,當發(fā)生異常后最終會在DispatcherServlet#processDispatchResult方法中進行處理。

public class DispatcherServlet extends FrameworkServlet {  /*    * 默認情況下有如下2個異常解析器    * 1. DefaultErrorAttributes    * 2. ExceptionHandlerExceptionResolver    */  private List 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

// 調(diào)用父類(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;    }    // 執(zhí)行異常處理方法的調(diào)用    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);  }  protected ServletInvocableHandlerMethod getExceptionHandlerMethod(...) {    Class handlerType = null;    if (handlerMethod != null) {        handlerType = handlerMethod.getBeanType();      // 緩存并設置當前執(zhí)行Class對應的ExceptionHandlerMethodResolver      // ExceptionHandlerMethodResolver構(gòu)造函數(shù)中會解析當前類中的所有方法是否有@ExceptionHandler注解      ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.computeIfAbsent(handlerType, ExceptionHandlerMethodResolver::new);      // 解析是否匹配當前發(fā)生的異常      Method method = resolver.resolveMethod(exception);      if (method != null) {        return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);      }    }    // 如果上面的執(zhí)行的Class中沒有找到對應處理器,那么就從全局的異常處理中進行查找匹配    // 這里的exceptionHandlerAdviceCache集合在類初始化執(zhí)行時已經(jīng)處理完成    for (Map.Entry 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;  }}

通過上面的源碼分析你應該知道了關(guān)于SpringMVC中異常處理的原理。

當上面的異常處理機制都沒法處理,那么將會調(diào)用默認的/error接口。

public class ErrorMvcAutoConfiguration {  @Bean  @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)  public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider 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在容器啟動時會自動注冊到內(nèi)嵌的容器中,如:Tomcat。

標簽:

圖片新聞

精彩新聞