SpringMVC異常處理機制


SpringMVC異常處理機制

springMVC會將所有在doDispatch方法中的異常捕獲,然后處理。無法處理的異常會拋出給容器處理。

在doDispatch()中調用processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)處理結果:包括出現和不出現異常的處理都放在這里面

下面是它的源碼

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) {

                  logger.debug("ModelAndViewDefiningException encountered", exception);

                  mv = ((ModelAndViewDefiningException) exception).getModelAndView();//若拋出的異常是人為的指定mv的,則直接執行

             }

             else {

                  Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);

                  mv = processHandlerException(request, response, handler, exception);

                  errorView = (mv != null);

             }

         }

 

         // Did the handler return a view to render?

         if (mv != null && !mv.wasCleared()) {

             render(mv, request, response);

             if (errorView) {

                  WebUtils.clearErrorRequestAttributes(request);

             }

         }

         else {

             if (logger.isDebugEnabled()) {

                  logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +

                          "': assuming HandlerAdapter completed request handling");

             }

         }

 

         if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

             // Concurrent handling started during a forward

             return;

         }

 

         if (mappedHandler != null) {

             mappedHandler.triggerAfterCompletion(request, response, null);

         }

    }

如果有異常出現,首先判斷是不是ModelAndViewDefiningException,這個異常是認為的定義的,指定了modelAndView,所以直接拿到該mv,執行就行。

如果不是ModelAndViewDefiningException,則要調用processHandlerException(request, response, handler, exception);方法處理。具體源碼如下:

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,

             Object handler, Exception ex) throws Exception {

         // Check registered HandlerExceptionResolvers...

         ModelAndView exMv = null;

         for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {

             exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);

             if (exMv != null) {

                  break;

             }

         }

         if (exMv != null) {

             if (exMv.isEmpty()) {

                  request.setAttribute(EXCEPTION_ATTRIBUTE, ex);

                  return null;

             }

             // We might still need view name translation for a plain error model...

             if (!exMv.hasView()) {

                  exMv.setViewName(getDefaultViewName(request));

             }

             if (logger.isDebugEnabled()) {

                  logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);

             }

             WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());

             return exMv;

         }

 

         throw ex;

    }

 

該方法會遍歷所有的handlerExceptionResolvers,判斷能否處理該異常。調用各自的resolveException 方法(一般拋出的異常都需要通過這一步,要么能被處理;要么處理不了,向上拋)。

則現在需要關注這些handlerExceptionResolvers是如何得到的,有什么分類呢?具體參考http://www.cnblogs.com/fangjian0423/p/springMVC-exception-analysis.html#ExceptionHandlerExceptionResolver

1,handlerExceptionResolver的結構如下圖所示

 

2,類的分析

根據上述結構,對SpringMVC異常處理類進行分析:

l  SimpleMappingException:根據配置進行解析異常的類,包括配置異常類型,默認的錯誤視圖,默認的響應碼,異常映射等配置屬性。

使用時只需要在xml配置文件中配置一下就可以

l  < mvc:annotation-driven />配置中定義的HandlerExceptionResolver實現類,我們看下< mvc:annotation-driven />配置解析類org.springframework.web.servlet.config

.AnnotationDrivenBeanDefinitionParser中部分代碼片段:

 

 

可以看到,當我們定義< mvc:annotation-driven >時,框架會自動將三個類ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver添加到DispatcherServlet中的handlerExceptionResolvers集合中。並且order分別為1,2,3,ExceptionHandlerExceptionResolver優先級最高,ResponseStatusExceptionResolver第二,DefaultHandlerExceptionResolver第三。

下面分別解析這三個類以及如何使用

(1)ExceptionHandlerExceptionResolver

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

一般使用的時候通過第二種方法,定義一個類並配上注解@ControllerAdvice。然后再該類中定義處理異常的方法並使用注解@ExceptionHandler

 

(2)ResponseStatusExceptionResolver

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

一般使用的時候,定義一個異常的時候,帶上@ResponseStatus注解,並設置響應狀態碼。

(3)DefaultHandlerExceptionResolver

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

 

l  自定義異常處理器

實現HandlerExceptionReslover或繼承AbstractHandlerExceptionResolver,將自定義的處理器配置到xml文件中,設置order。

 

 

3,使用

l  SimpleMappingException

在spring-mvc.xml中定義

可以設置order,defaultErrorView,exceptionAttribute,exceptionMappings

如下所示

<bean

         class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

         <!-- 配置order為-1,表示優先級最高 -->

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

         <!-- 定義默認的異常處理頁面,當該異常類型的注冊時使用 -->

         <property name="defaultErrorView" value="error"></property>

         <!-- 定義異常處理頁面用來獲取異常信息的變量名,默認名為exception -->

         <property name="exceptionAttribute" value="ex"></property>

         <!-- 定義需要特殊處理的異常,用類名或完全路徑名作為key,異常也頁名作為值 -->

         <property name="exceptionMappings">

             <props>

                  <prop key="yellow.exception.BusinessException">error-business</prop>

                  <prop key="yellow.exception.ParameterException">error-parameter</prop>

                  <!-- 這里還可以繼續擴展對不同異常類型的處理 -->

             </props>

         </property>

</bean>

 

l  使用@ControllerAdvice,@ExceptionHandler,@ResponseStatuts

@ControllerAdvice

public class AControllerAdvice {

    @ExceptionHandler(BusinessException.class)

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ResponseBody

    public String handleException1() {

         return "1,處理Business異常";

    }

 

    @ExceptionHandler(Exception.class)

    @ResponseStatus(HttpStatus.BAD_REQUEST)

    @ResponseBody

    public String handleException() {

         return "2,處理一般異常";

    }

 

}

 

當出現BusinessException時,結果如下

 

 

可以發現,返回了400 bad request狀態碼,表示@ResponseStatus設置生效了。

l  自定義異常處理器:由於第二種方法已經很好的滿足了處理異常的需求,一般項目中就不會自定義的處理器了。使用也很簡單,只需覆蓋doResolveException方法就可以了。

如下所示:如果想返回json,可以使用return new ModelAndView(new FastJsonJsonView(), map);來實現。

public class MyExceptionHandlerWithOrder extends AbstractHandlerExceptionResolver {

 

    @Override

    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,

             Exception ex) {

         // TODO Auto-generated method stub

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

         map.put("type", ex.getClass().getName());

         map.put("message", ex.getMessage());

         return new ModelAndView(new FastJsonJsonView(), map);

    }

 

}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM