Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 396|回复: 0

SpringMVC异常处理机制

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-5-29 13:50:21 | 显示全部楼层 |阅读模式

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

     

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-2 18:54 , Processed in 0.065143 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表