• <em id="mufrm"></em>
    <rp id="mufrm"></rp><legend id="mufrm"></legend><dd id="mufrm"></dd><rp id="mufrm"><object id="mufrm"><input id="mufrm"></input></object></rp><tbody id="mufrm"><pre id="mufrm"></pre></tbody>
  • 
    

    <s id="mufrm"></s>

    面試官:你能給我談談Spring MVC的異常處理機制嗎?

    :2020年04月02日 Java面試那些事兒
    分享到:

    # 前言SpringMVC是目前主流的Web MVC框架之一。 本文將分析SpringMVC的異常處理內容,讓讀者了解SpringMVC異常處理的設計原理。# 重要接口和類介紹1. HandlerExceptionResolver接口SpringMVC異...

    # 前言

    SpringMVC是目前主流的Web MVC框架之一。 

    本文將分析SpringMVC的異常處理內容,讓讀者了解SpringMVC異常處理的設計原理。

    # 重要接口和類介紹

    1. HandlerExceptionResolver接口

    SpringMVC異常處理核心接口。該接口定義了1個解析異常的方法:

    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;

    2. AbstractHandlerExceptionResolver抽象類

    實現了HandlerExceptionResolver和Ordered接口的抽象類。

    先看下屬性:

    再看下接口的實現:

    3. AbstractHandlerMethodExceptionResolver抽象類

    繼承AbstractHandlerExceptionResolver抽象類的抽象類。該類主要就是為HandlerMethod類服務,既handler參數是HandlerMethod類型。

    該類重寫了shouldApplyTo方法:

    doResolveException抽象方法的實現中調用了doResolveHandlerMethodException方法,該方法也是1個抽象方法。

    4. ExceptionHandlerExceptionResolver類

    繼承自AbstractHandlerMethodExceptionResolver,該類主要處理Controller中用@ExceptionHandler注解定義的方法。

    該類也是<annotation-driven/>配置中定義的HandlerExceptionResolver實現類之一,大多數異常處理都是由該類操作。

    該類比較重要,我們來詳細講解一下。

    首先我們看下這個類的屬性:

    再來看下該類的doResolveHandlerMethodException抽象方法的實現:

    默認的HandlerMethodArgumentResolver集合:

    默認的HandlerMethodReturnValueHandler集合:

    我們進入getExceptionHandlerMethod方法看看是如何得到ServletInvocableHandlerMethod的:

    我們看到getExceptionHandlerMethod中會實例化ExceptionHandlerMethodResolver,我們看看這個類到底是什么東西?

    ExceptionHandlerMethodResolver是一個會在Class及Class的父類集合中找出帶有@ExceptionHandler注解的類,該類帶有key為Throwable,value為Method的緩存屬性。

    ExceptionHandlerMethodResolver的構造過程:

    ExceptionHandlerExceptionResolver處理過程總結一下:根據用戶調用Controller中相應的方法得到HandlerMethod,之后構造ExceptionHandlerMethodResolver,構造ExceptionHandlerMethodResolver有2種選擇,1.通過HandlerMethod拿到Controller,找出Controller中帶有@ExceptionHandler注解的方法(局部) 2.找到@ControllerAdvice注解配置的類中的@ExceptionHandler注解的方法(全局)。這2種方式構造的ExceptionHandlerMethodResolver中都有1個key為Throwable,value為Method的緩存。之后通過發生的異常找出對應的Method,然后調用這個方法進行處理。這里異常還有個優先級的問題,比如發生的是NullPointerException,但是聲明的異常有Throwable和Exception,這時候ExceptionHandlerMethodResolver找Method的時候會根據異常的最近繼承關系找到繼承深度最淺的那個異常,即Exception。

    5. DefaultHandlerExceptionResolver類

    繼承自AbstractHandlerExceptionResolver抽象類。<annotation-driven/>配置中定義的HandlerExceptionResolver實現類之一。

    該類的doResolveException方法中主要對一些特殊的異常進行處理,比如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等。

    6. ResponseStatusExceptionResolver類

    繼承自AbstractHandlerExceptionResolver抽象類。<annotation-driven/>配置中定義的HandlerExceptionResolver實現類之一。

    該類的doResolveException方法主要在異常及異常父類中找到@ResponseStatus注解,然后使用這個注解的屬性進行處理。

    說明一下為什么ExceptionHandlerExceptionResolver、DefaultHandlerExceptionResolver、ResponseStatusExceptionResolver是<annotation-driven/>配置中定義的HandlerExceptionResolver實現類。

    我們看下<annotation-driven/>配置解析類AnnotationDrivenBeanDefinitionParser中部分代碼片段:

    這3個ExceptionResolver最終被會加入到DispatcherServlet中的handlerExceptionResolvers集合中。

    其中ExceptionHandlerExceptionResolver優先級最高,ResponseStatusExceptionResolver第二,DefaultHandlerExceptionResolver第三。

    為什么ExceptionHandlerExceptionResolver優先級最高,因為order屬性值最低

    7. @ResponseStatus注解

    讓1個方法或異常有狀態碼(status)和理由(reason)返回。這個狀態碼是http響應的狀態碼。

    8. SimpleMappingExceptionResolver類

    繼承自AbstractHandlerExceptionResolver抽象類,是1個根據配置進行解析異常的類,包括配置異常類型,默認的錯誤視圖,默認的響應碼,異常映射等配置屬性。本文不分析,有興趣的讀者可自行查看源碼。

    # 源碼分析

    下面我們來分析SpringMVC處理異常的源碼。

    SpringMVC在處理請求的時候,通過RequestMappingHandlerMapping得到HandlerExecutionChain,然后通過RequestMappingHandlerAdapter得到1個ModelAndView對象,這在之前發生的異常都會被catch到,然后得到這個異常并作為參數傳入到processDispatchResult方法處理。

    processDispatchResult方法如下:

    processHandlerException方法:

    # 實例講解

    接下里講常用ExceptionResolver的實例。

    1. ExceptionHandlerExceptionResolver

    @Controller

    @RequestMapping(value = "/error")

    public class TestErrorController {

    @RequestMapping("/exception")

    public ModelAndView exception(ModelAndView view) throws ClassNotFoundException {

    view.setViewName("index");

    throw new ClassNotFoundException("class not found");

    }

    @RequestMapping("/nullpointer")

    public ModelAndView nullpointer(ModelAndView view) {

    view.setViewName("index");

    String str = null;

    str.length();

    return view;

    }

    @ExceptionHandler(RuntimeException.class)

    public ModelAndView error(RuntimeException error, HttpServletRequest request) {

    ModelAndView mav = new ModelAndView();

    mav.setViewName("error");

    mav.addObject("param", "Runtime error");

    return mav;

    }

    @ExceptionHandler()

    public ModelAndView error(Exception error, HttpServletRequest request, HttpServletResponse response) {

    ModelAndView mav = new ModelAndView();

    mav.setViewName("error");

    mav.addObject("param", "Exception error");

    return mav;

    }

    /**

    @ExceptionHandler(NullPointerException.class)

    public ModelAndView error(ModelAndView mav) {

    mav.setViewName("error");

        mav.addObject("param", "NullPointer error");

    return mav;

    }*/

    }

    分析一下:

    如果用戶進入nullpointer方法,str對象還未初始化,會發生NullPointerException。如果去掉最后1個注釋掉的error方法,那么會報錯。因為ExceptionHandlerExceptionResolver的默認HandlerMethodArgumentResolver中只有ServletRequestMethodArgumentResolver和ServletResponseMethodArgumentResolver(所以其他2個error方法中的request和response參數沒問題)。所以我們給最后1個error方法加了注釋。

    由于TestErrorController控制器中有2個帶有@ExceptionHandler注解的方法,之前分析的ExceptionHandlerMethodResolver構造過程中,會構造ExceptionHandlerMethodResolver,ExceptionHandlerMethodResolver內部會有1個key分別為RuntimeException和Exception,value分別為第一個和第二個error方法的緩存。由于NullPointerException的繼承關系離RuntimeException比Exception近,因此最終進入了第一個error方法。

    如果用戶進入exception方法,同理。ClassNotFoundException繼承自Exception,跟RuntimeException沒關系,那么進入第二個error方法。

    說明一下,兩個error方法返回值都是ModelAndView,這是因為ExceptionHandlerMethodResolver的默認HandlerMethodReturnValueHandler中有ModelAndViewMethodReturnValueHandler。還有其他的比如ModelMethodProcessor、ViewMethodReturnValueHandler和ViewNameMethodReturnValueHandler等。這3個分別代表返回值Model,View和字符串。有興趣的讀者可自行查看源碼。 

    上個例子是基于Controller的@ExceptionHandler注解方法,每個Controller都需要寫@ExceptionHandler注解方法(寫個BaseController可不用每個Controller都寫單獨的@ExceptionHandler注解方法)。

    ExceptionHandlerMethodResolver內部找不到Controller的@ExceptionHandler注解的話,會找@ControllerAdvice中的@ExceptionHandler注解方法。

    因此,我們也可以寫1個ControllerAdvice。

    @ControllerAdvice

    public class ExceptionControllerAdvice {

    @ExceptionHandler(Throwable.class)

    @ResponseBody

    public Map<String, Object> ajaxError(Throwable error, HttpServletRequest request, HttpServletResponse response) {

    Map<String, Object> map = new HashMap<String, Object>();

    map.put("error", error.getMessage());

    map.put("result", "error");

    return map;

    }

    }

    此類中的error方法代表著全局異常處理方法。

    該方法可對ajax操作進行異常處理,我們返回值使用了@ResponseBody進行了處理,然后配置json消息轉換器即可,這樣該方法響應給客戶端的數據就變成了json數據。

    2. ResponseStatusExceptionResolver

    先定義1個自定義異常:

    @ResponseStatus(HttpStatus.UNAUTHORIZED)

    public class UnauthorizedException extends RuntimeException {

    }

    Controller代碼:

    @Controller

    @RequestMapping(value = "/error")

    public class TestErrorController {

    @RequestMapping("/unauth")

    public ModelAndView unauth(ModelAndView view) {

    view.setViewName("index");

    throw new UnauthorizedException();

    }

    }

    由于該類沒有寫@ExceptionHandler注解,因此ExceptionHandlerExceptionResolver不能解析unauth觸發的異常。接下來由ResponseStatusExceptionResolver進行解析,由于觸發的異常UnauthorizedException帶有@ResponseStatus注解。因此會被ResponseStatusExceptionResolver解析到。最后響應HttpStatus.UNAUTHORIZED代碼給客戶端。HttpStatus.UNAUTHORIZED代表響應碼401,無權限。關于其他的響應碼請參考HttpStatus枚舉類型源碼。

    3. DefaultHandlerExceptionResolver

    直接上代碼:

    @Controller

    @RequestMapping(value = "/error")

    public class TestErrorController {

    @RequestMapping("/noHandleMethod")

    public ModelAndView noHandleMethod(ModelAndView view, HttpServletRequest request) throws NoSuchRequestHandlingMethodException {

    view.setViewName("index");

    throw new NoSuchRequestHandlingMethodException(request);

    }

    }

    用戶進入noHandleMethod方法觸發NoSuchRequestHandlingMethodException異常,由于沒配置@ExceptionHandler以及該異常沒有@ResponseStatus注解,最終由DefaultHandlerExceptionResolver解析,由于NoSuchRequestHandlingMethodException屬于DefaultHandlerExceptionResolver解析的異常,因此被DefaultHandlerExceptionResolver解析。NoSuchRequestHandlingMethodException會發生404錯誤。

    關于DefaultHandlerExceptionResolver可以處理的其他異常,請參考DefaultHandlerExceptionResolver源碼。

    # 擴展ExceptionHandlerExceptionResolver功能

    SpringMVC提供的HandlerExceptionResolver基本上都能滿足我們的開發要求,因此本文就不準備寫自定義的HandlerExceptionResolver。

    既然不寫自定義的HandlerExceptionResolver,我們就來擴展ExceptionHandlerExceptionResolver來吧,讓它支持更多的功能。

    比如為ExceptionHandlerExceptionResolver添加更多的HandlerMethodArgumentResolver,ExceptionHandlerExceptionResolver默認只能有2個HandlerMethodArgumentResolver和ServletRequestMethodArgumentResolver(處理ServletRequest、WebRequest、MultipartRequest、HttpSession等參數)和ServletResponseMethodArgumentResolver(處理ServletResponse、OutputStream或Writer參數)。

    ModelAndView這種類型的參數會被ServletModelAttributeMethodProcessor處理。因此我們需要給ExceptionHandlerExceptionResolver添加ServletModelAttributeMethodProcessor這個HandlerMethodArgumentResolver。由于ServletModelAttributeMethodProcessor處理ModelAndView參數會使用WebDataBinderFactory參數,因此我們得重寫doResolveHandlerMethodException方法,所以新寫了1個類繼承ExceptionHandlerExceptionResolver。

    public class MyExceptionHandlerExceptionResolver extends ExceptionHandlerExceptionResolver{

    public MyExceptionHandlerExceptionResolver() {

    List<HandlerMethodArgumentResolver> list = new ArrayList<HandlerMethodArgumentResolver>();

    list.add(new ServletModelAttributeMethodProcessor(true));

    this.setCustomArgumentResolvers(list);

    }

    @Override

    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,

    HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {

    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);

    if (exceptionHandlerMethod == null) {

    return null;

    }

    //ServletModelAttributeMethodProcessor 內部會使用傳遞進來的WebDataBinderFactory參數,該參數由ServletInvocableHandlerMethod提供

    exceptionHandlerMethod.setDataBinderFactory(new ServletRequestDataBinderFactory(null, null));

    exceptionHandlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers());

    exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(getReturnValueHandlers());

    ServletWebRequest webRequest = new ServletWebRequest(request, response);

    ModelAndViewContainer mavContainer = new ModelAndViewContainer();

    try {

    if (logger.isDebugEnabled()) {

    logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);

    }

    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);

    }

    catch (Exception invocationEx) {

    logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);

    return null;

    }

    if (mavContainer.isRequestHandled()) {

    return new ModelAndView();

    }

    else {

    ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());

    mav.setViewName(mavContainer.getViewName());

    if (!mavContainer.isViewReference()) {

    mav.setView((View) mavContainer.getView());

    }

    return mav;

    }

    }

    }

    配置:

    <bean class="org.format.demo.support.exceptionResolver.MyExceptionHandlerExceptionResolver">

    <property name="order" value="-1"/>

    </bean>

    配置完成之后,然后去掉本文實例講解中ExceptionHandlerExceptionResolver的代碼,并去掉支持NullPointerException異常的那個方法的注釋。

    測試如下:

    讀者可根據需求自己實現其他的擴展功能?;蛘邔崿FHandlerExceptionResolver接口新寫1個HandlerExceptionResolver實現類。

    新的的HandlerExceptionResolver實現類只需在配置文件中定義即可,然后配置優先級。DispatcherServlet初始化HandlerExceptionResolver的時候會自動尋找容器中實現了HandlerExceptionResolver接口的類,然后添加進來。

    # 總結

    分析了SpringMVC的異常處理機制并介紹了幾個重要的接口和類,并分析了在<annotation-driven/>中定義的3個常用的HandlerExceptionResolver。

    之后又編寫了1個繼承自ExceptionHandlerExceptionResolver類的異常解析類,鞏固了之前分析的知識。

    希望這篇文章能幫助讀者了解SpringMVC異常機制。

    文中難免有錯誤,希望讀者能夠指明出來。

    # 參考資料

    • http://docs.spring.io/spring/docs/4.0.5.RELEASE/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers

    [我要糾錯]
    文:王振袢&發表于江蘇
    關鍵詞: 前言 SpringMVC 目前 流的 框架

    來源:本文內容搜集或轉自各大網絡平臺,并已注明來源、出處,如果轉載侵犯您的版權或非授權發布,請聯系小編,我們會及時審核處理。
    聲明:江蘇教育黃頁對文中觀點保持中立,對所包含內容的準確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點負責,僅作分享之用,文章版權及插圖屬于原作者。

    點個贊
    0
    踩一腳
    0

    您在閱讀:面試官:你能給我談談Spring MVC的異常處理機制嗎?

    Copyright?2013-2022 JSedu114 All Rights Reserved. 江蘇教育信息綜合發布查詢平臺保留所有權利

    蘇公網安備32010402000125 蘇ICP備14051488號-3南京思必達教育科技有限公司版權所有

    南京思必達教育科技有限公司版權所有   百度統計

    亚洲色欲色欱www在线_久久精品娱乐亚洲领先_欧美老妇牲交vid0_青青青国产费观看视频
  • <em id="mufrm"></em>
    <rp id="mufrm"></rp><legend id="mufrm"></legend><dd id="mufrm"></dd><rp id="mufrm"><object id="mufrm"><input id="mufrm"></input></object></rp><tbody id="mufrm"><pre id="mufrm"></pre></tbody>
  • 
    

    <s id="mufrm"></s>
    最熱文章
    最新文章
    • 卡爾蔡司鏡片優惠店,鏡片價格低
    • 蘋果原裝手機殼