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

    }

}

SpringMVC异常处理机制的更多相关文章

  1. SpringMVC异常处理机制详解[附带源码分析]

    目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...

  2. springMVC源码分析--异常处理机制HandlerExceptionResolver执行原理(二)

    上一篇博客springMVC源码分析--异常处理机制HandlerExceptionResolver简单示例(一)中我们简单地实现了一个异常处理实例,接下来我们要介绍一下HandlerExceptio ...

  3. 分享知识-快乐自己:Spring中的(三种)异常处理机制

    案例目录结构: Web.xml 配置: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application ...

  4. springmvc异常处理解析#ExceptionHandlerExceptionResolver

    开头 试想一下我们一般怎么统一处理异常呢,答:切面.但抛开切面不讲,如果对每一个controller方法抛出的异常做专门处理,那么着实太费劲了,有没有更好的方法呢?当然有,就是本篇文章接下来要介绍的s ...

  5. Java异常处理机制 try-catch-finally 剖析

    Java拥有着强大的异常处理机制,最近初步学习了下,感觉内容还是挺多的,特此来将自己的理解写出来与大家分享. 一. 在Java代码code中,由于使用Myeclipse IDE,可以自动提醒用户哪里有 ...

  6. JAVA 异常处理机制

    主要讲述几点: 一.异常的简介 二.异常处理流程 三.运行时异常和非运行时异常 四.throws和throw关键字 一.异常简介 异常处理是在程序运行之中出现的情况,例如除数为零.异常类(Except ...

  7. 深入理解java异常处理机制

       异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的 ...

  8. C++学习笔记27:异常处理机制

    一.异常处理机制基础 异常的定义 程序中可以检测的运行不正常的情况 异常处理的基本流程 某段程序代码在执行操作时发生特殊情况,引发一个特定的异常 另一段程序代码捕获该异常并处理它 二.异常的引发 th ...

  9. C++中的异常处理机制

    C++中的捕获异常机制catch参数中实参的类型不同,采取的处理方式则不相同,且与普通的函数调用还不一样,具体表现为当抛出异常throw A()或throw obj时,对象会进行一次额外的对象复制操作 ...

随机推荐

  1. [ACM_数据结构] HDU 1166 敌兵布阵 线段树 或 树状数组

    #include<iostream> #include<cstdio> #include<memory.h> using namespace std; ]; //- ...

  2. 阿里巴巴集团2013实习生招聘技术类笔试卷(B)

    一.单选题 1.在常用的网络协议中,___是面向连接的.有重传功能的协议. A.IP   B.TCP    C.UDP    D.DXP 2.500张多米诺骨牌整齐地排成一列,依顺序编号为1.2.3… ...

  3. SqlDataReader 结果集 转成 DataTable

    /// <summary> /// SqlDataReader 转成 DataTable /// 源需要是结果集 /// </summary> /// <param na ...

  4. 一个简单的QQ隐藏图生成算法

    隐藏图不是什么新鲜的东西,具体表现在大部分社交软件中,预览图看到的是一张图,而点开后看到的又是另一张图.虽然很早就看到过这类图片,但是一直没有仔细研究过它的原理,今天思考了一下,发现挺有趣的,所以自己 ...

  5. Android studio项目预览的时候提示错误ActionBarOverlayLayout

    android studio打开项目(别人的demo),提示页面没法预览.截图如下 根据查询,是主题没法正常显示,需要修改样式.样式文件的路径为res\values\styles.xml,截图如下. ...

  6. 在MUI框架中使用video.js插件,并在暂停的时候利用Asp.net将观看时长保存到sqlserver数据库

    本次保存数据的情况有三种: 在视频播放的时候点击暂停,将本视频的进度保存到数据库 利用mui内部的控件,返回上一页操作时,进行保存 安卓手机触发返回键的时候,进行保存 示例一: 在video标签上面添 ...

  7. EF学习笔记-1 EF增删改查

    首次接触Entity FrameWork,就感觉非常棒.它节省了我们以前写SQL语句的过程,同时也让我们更加的理解面向对象的编程思想.最近学习了EF的增删改查的过程,下面给大家分享使用EF对增删改查时 ...

  8. powerviot install in sharepoint 2013

    1.在app server安装powerviot for sharepoint,然后进行配置powerviot 2.powerviot配置之前需要设置security token service以及e ...

  9. 洛谷P4197 Peaks&&克鲁斯卡尔重构树学习笔记(克鲁斯卡尔重构树+主席树)

    传送门 据说离线做法是主席树上树+启发式合并(然而我并不会) 据说bzoj上有强制在线版本只能用克鲁斯卡尔重构树,那就好好讲一下好了 这里先感谢LadyLex大佬的博客->这里 克鲁斯卡尔重构树 ...

  10. VSCode保存插件配置并使用 gist 管理代码片段

    setting sync 保存配置 由于公司和家里都使用 VSCode 作为主要编辑器,同步配置是最紧要的.VSCode 提供了setting sync插件,很方便我们同步插件配置.引用网上教程: 在 ...