Administrator
发布于 2022-10-24 / 64 阅读
0
0

SpringMVC异常处理

概述

Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 
Handler 映射、数据绑定以及目标方法执行时发生的异常。

SpringMVC 提供的 HandlerExceptionResolver 的实现类:

image-1666603229605
 
DispatcherServlet 默认装配

image-1666603304870

自定义错误页面信息

如果按SpringMVC或者Tomcat的默认错误页面,你不喜欢的话,可以自定义错误信息。
eg:数学运算异常

image-1666604176013

1、ExceptionHandlerExceptionResolver

作用

主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。

@ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,
但是声明的异常有 RuntimeException 和 Exception,
此时会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler 注解方法,
即标记了 RuntimeException 的方法

ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,
会找 @ControllerAdvice 中的@ExceptionHandler 注解方法

controller

@Controller
public class ErrorController {

    @RequestMapping("/errorTest")
    public String errorhandle01(ModelAndView modelAndView){
        int i = 10/0;
        return "success";
    }


    @ExceptionHandler(ArithmeticException.class)
    public ModelAndView handleException01(Exception e) {
        System.out.println("数学运算异常---");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("myerror");
        modelAndView.addObject("ex",e);
        return modelAndView;
    }
    -------------------
    //两个都能处理数学运算异常,按精度优先匹配。所有异常处理都是精度优先
    ----------------------

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException02(Exception e) {
        System.out.println("Exception---");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("myerror");
        modelAndView.addObject("ex",e);
        return modelAndView;
    }
}

jsp

<body>
错误信息:${ex}
</body>

结果

image-1666611048718
 
补充:异常对象不能通过Map集合方式传递给成功页面。(Map不可以),即之前说的入参直接放model,map,modelandview三个都只有Model旗下的可以,所以想将异常信息返回给页面,可以使用new ModelAndView的方式或者具体按ExceptionHandlerExceptionResolver支持的参数解析器的条件来操作携带信息

image-1666612432682

 

返回ModelAndView可以携带信息

else {
--
//在异常处理方法执行的时候,会new一个ModelAndViewContainer,执行后,方法内的信息会携带在里
//然后会在下面进行赋值处理,统一返回ModelAndView给页面进行渲染
--
			ModelMap model = mavContainer.getModel();
			HttpStatus status = mavContainer.getStatus();
			ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
			mav.setViewName(mavContainer.getViewName());
			if (!mavContainer.isViewReference()) {
				mav.setView((View) mavContainer.getView());
			}

 

抽取公共的处理异常@ControllerAdvice

以上的写法是写在一个控制器的内部,只能处理当前控制器发生的异常,
这是显然是不符合可重用原则的,所以,可以对某些不是比较特殊的异常出里进行抽取
@ControllerAdvice注解

eg:

@ControllerAdvice
public class ExceptionAdviceHandler {


    @ExceptionHandler(value={java.lang.ArithmeticException.class})
    public ModelAndView handleException(Exception ex){

    System.out.println("公共的处理异常ArithmeticException:"+ex);
    ModelAndView mv = new ModelAndView("myerror");
    mv.addObject("ex", ex);

    return mv;
    }

    @ExceptionHandler(value={java.lang.Exception.class})
    public ModelAndView handleException2(Exception ex){

        System.out.println("公共的处理异常Exception:"+ex);
        ModelAndView mv = new ModelAndView("myerror");
        mv.addObject("ex", ex);

        return mv;
    }
}

内部和公共的处理优先级问题

只要内部的异常处理能处理发生的异常,就轮不到公共异常处理类,另:无论是内部的还是公共的,异常处理都按精度优先

2、ResponseStatusExceptionResolver

作用

若在处理器方法中抛出异常:若ExceptionHandlerExceptionResolver 解析不了异常。
由于触发的异常类型带有@ResponseStatus 注解。因此会被
ResponseStatusExceptionResolver 解析到。
最后响应状态代码和信息给客户端。

eg:@ResponseStatus标志的异常处理类

@ResponseStatus(value= HttpStatus.NOT_ACCEPTABLE,reason="数学运算异常")
public class RuntimeExceptionTest extends RuntimeException{
}
        try {
            int i = 10/0;
        }catch (Exception e) {
        --
        //抛出一个自定义的异常,这个异常若ExceptionHandlerExceptionResolver解析不了
        //但又有@ResponseStatus 注解
        --
            throw new RuntimeExceptionTest();
        }

结果:

image-1666615666898

@ResponseStatus标在方法上

    @ResponseStatus(value= HttpStatus.NOT_ACCEPTABLE,reason="数学运算异常")
    @RequestMapping("/responseStatus")
    public String errorhandle02(){
        System.out.println("responseStatus");
        return "success";
    }

返回结果:

image-1666615914329

控制台输出:

image-1666615931620

原理

image-1666660483587

标在方法上一样作为方法执行,只是如果    
@ResponseStatus(value= HttpStatus.NOT_ACCEPTABLE,reason="数学运算异常")中有reason的值,
会直接返回。mavContainer也标记此请求已被完全处理

 

3、DefaultHandlerExceptionResolver

对一些特殊的异常进行处理,比如:

NoSuchRequestHandlingMethodException、

HttpRequestMethodNotSupportedException、

HttpMediaTypeNotSupportedException、

HttpMediaTypeNotAcceptableException等。

eg:

<a href="/errorhandle03">errorhandle03</a>


    @RequestMapping(value = "/errorhandle03" ,method = RequestMethod.POST)
    public String errorhandle03(){
        System.out.println("errorhandle03");
        return "success";
    }

结果

image-1666616342666

另:SimpleMappingExceptionResolver

作用

如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常

web.xml

 <bean id="mappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
 --
 //private Properties exceptionMappings;
 //SimpleMappingExceptionResolver的exceptionMappings指定处理哪些异常
 --
            <property name="exceptionMappings">
                <props>
                --
                //指定响应的错误页面
                --
                    <prop key="java.lang.ArithmeticException">myerror</prop>
                </props>
            </property>
            --
            //private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
            //指定返回携带错误信息的属性名,默认是exception
            --
        <property name="exceptionAttribute" value="ex"></property>
    </bean>

原理:

  protected ModelAndView doResolveException(
          HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

      // Expose ModelAndView for chosen error view.
      String viewName = determineViewName(ex, request);
      if (viewName != null) {
          // Apply HTTP status code for error views, if specified.
          // Only apply it if we're processing a top-level request.
          Integer statusCode = determineStatusCode(request, viewName);
          if (statusCode != null) {
              applyStatusCodeIfPossible(request, response, statusCode);
          }
          --
          //最终会将错误信息放在ModelAndView中并返回,
          //所以错误信息在页面可以使用${requestScope.exception}获取
          --
          return getModelAndView(viewName, ex, request);
      }
      else {
          return null;
      }
  }

总结

SpringMVC的异常处理就是:从容器中找异常解析器,这个异常解析器作为九大组件之一,
如果你配了,就用你配的,如果你没配,就用默认的实现策略。
处理的流程就是:容器中的解析器逐个遍历,能处理的就返回

        if (this.handlerExceptionResolvers != null) {
            for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
                exMv = resolver.resolveException(request, response, handler, ex);
                if (exMv != null) {
                    break;
                }
            }
        }
        
      三个默认的解析器中;DefaultHandlerExceptionResolver 优先级最低
      所以前面两个不能处理的错误它一般都能处理,如果它都不能处理,那就会扔给Tomcat

评论