<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 前言 本章將分析SpringMVC的異常處理內容,讓讀者了解SpringMVC異常處理的設計原理。 # 重要接口和類介紹 1.**HandlerExceptionResolver接口** SpringMVC異常處理核心接口。該接口定義了1個解析異常的方法: ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; 2.**AbstractHandlerExceptionResolver抽象類** 實現了HandlerExceptionResolver和Ordered接口的抽象類。 先看下屬性: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis01.png) 再看下接口的實現: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis02.png) 3.**AbstractHandlerMethodExceptionResolver抽象類** 繼承AbstractHandlerExceptionResolver抽象類的抽象類。 該類主要就是為HandlerMethod類服務,既handler參數是HandlerMethod類型。 該類重寫了shouldApplyTo方法: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis03.png) doResolveException抽象方法的實現中調用了doResolveHandlerMethodException方法,該方法也是1個抽象方法。 4.**ExceptionHandlerExceptionResolver類** 繼承自AbstractHandlerMethodExceptionResolver,**該類主要處理Controller中用@ExceptionHandler注解定義的方法。該類也是annotation-driven標簽配置中定義的HandlerExceptionResolver實現類之一,大多數異常處理都是由該類操作。** 該類比較重要,我們來詳細講解一下。 首先我們看下這個類的屬性: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis04.png) 再來看下該類的doResolveHandlerMethodException抽象方法的實現: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis05.png) 默認的HandlerMethodArgumentResolver集合: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis06.png) 默認的HandlerMethodReturnValueHandler集合: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis07.png) 其中關于HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler接口,HandlerMethodArgumentResolverComposite、HandlerMethodReturnValueHandlerComposite類,HandlerMethod、ServletInvocableHandlerMethod類等相關知識的請參考[解析SpringMVC中Controller的方法中參數的工作原理](333548) 我們進入getExceptionHandlerMethod方法看看是如何得到ServletInvocableHandlerMethod的: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis08.png) 我們看到getExceptionHandlerMethod中會實例化ExceptionHandlerMethodResolver,我們看看這個類到底是什么東西? **ExceptionHandlerMethodResolver是一個會在Class及Class的父類集合中找出帶有@ExceptionHandler注解的類,該類帶有key為Throwable,value為Method的緩存屬性。** ExceptionHandlerMethodResolver的構造過程: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis09.png) ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis10.png) **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注解,然后使用這個注解的屬性進行處理。 ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis11.png) 說明一下為什么ExceptionHandlerExceptionResolver、DefaultHandlerExceptionResolver、ResponseStatusExceptionResolver是annotation-driven標簽配置中定義的HandlerExceptionResolver實現類。 我們看下annotation-driven標簽配置解析類AnnotationDrivenBeanDefinitionParser中部分代碼片段: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis12.png) 這3個ExceptionResolver最終被會加入到DispatcherServlet中的handlerExceptionResolvers集合中。 其中ExceptionHandlerExceptionResolver優先級最高,ResponseStatusExceptionResolver第二,DefaultHandlerExceptionResolver第三。 為什么ExceptionHandlerExceptionResolver優先級最高,因為order屬性值最低,這部分的知識請參考:[Spring中Ordered接口簡介](spring/Spring-Ordered-interface.md) 7.**@ResponseStatus注解** 讓1個方法或異常有狀態碼(status)和理由(reason)返回。這個狀態碼是http響應的狀態碼。 ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis13.png) 8.**SimpleMappingExceptionResolver類** 繼承自AbstractHandlerExceptionResolver抽象類,是1個根據配置進行解析異常的類,包括配置異常類型,默認的錯誤視圖,默認的響應碼,異常映射等配置屬性。 本章不分析,有興趣的讀者可自行查看源碼。 # 源碼分析 下面我們來分析SpringMVC處理異常的源碼。 SpringMVC在處理請求的時候,通過RequestMappingHandlerMapping得到HandlerExecutionChain,然后通過RequestMappingHandlerAdapter得到1個ModelAndView對象,這在之前發生的異常都會被catch到,然后得到這個異常并作為參數傳入到processDispatchResult方法處理。 processDispatchResult方法如下: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis14.png) processHandlerException方法: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis15.png) # 實例講解 接下里講常用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方法。 ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis16.png) 如果用戶進入exception方法,同理。ClassNotFoundException繼承自Exception,跟RuntimeException沒關系,那么進入第二個error方法。 ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis17.png) 說明一下,兩個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數據。 這方面的知識請參考:[解析SpringMVC關于json、xml自動轉換的原理](333549) **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枚舉類型源碼。 ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis18.png) **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源碼。 ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis19.png) # 擴展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異常的那個方法的注釋。 測試如下: ![](http://7x2wh6.com1.z0.glb.clouddn.com/springmvc-exception-analysis20.png) 讀者可根據需求自己實現其他的擴展功能。 或者實現HandlerExceptionResolver接口新寫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](http://docs.spring.io/spring/docs/4.0.5.RELEASE/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看