美文网首页Java
SpringMVC学习笔记 | 关于异常处理

SpringMVC学习笔记 | 关于异常处理

作者: 一颗白菜_ | 来源:发表于2019-11-20 13:26 被阅读0次

    SpringMVC通过HandlerExceptionResolver处理程序的异常,包括Handler映射、数据绑定以及目标方法执行时发生的异常。
    DispatcherServlet默认装配的HandlerExceptionResoolver实现类:

    • 没有使用<mvc:annotation-driven />配置
      (1)AnnotationMethodHandlerExceptionResolver
      (2)ResponseStatusExceptionResolver
      (3)DefaultHandlerExceptionResolver
    • 使用了<mvc:annotation-driven />配置
      (1)ExceptionHandlerExceptionResolver
      (2)ResponseStatusExceptionResolver
      (3)DefaultHandlerExceptionResolver

    ExceptionHandlerExceptionResolver

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

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

    如果ExceptionHandlerExceptionResolver内部找不到@ExceptionHandler注解的话,会找@ControllerAdvice注解的类中的@ExceptionHandler注解方法。

    下面我们通过例子来说明:
    我们首先先定义一个出现异常的方法:

    @RequestMapping(value = "/testExceptionHandlerExceptionResolver")
        public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
            System.out.println("res:"+10/i);
            return "success";
        }
    

    该方法接受一个整型参数并且将10与参数相除,因此我们只需要传入一个0参数即可发生异常。如果此处没有定义异常,然后我们直接访问http://localhost:8080/springmvc_test/testExceptionHandlerExceptionResolver?i=0,会出错:

    我们再定义一个处理异常的方法

        @ExceptionHandler({ArithmeticException.class})
        public String handleArithmeticException(Exception e){
            System.out.println("出异常了:"+e);
    
            return "error";
        }
    

    该方法会接受异常,并处理异常,在控制台中打印异常信息并且跳转到error.jsp。

    如果我们想要在页面上显示异常信息,我们可能一开始想到的会是在方法的入参中加入一个Map,但是这样是不可行的。因此我们要使用另外一种方法,返回一个ModelAndView对象,因此上面的方法修改为如下:

        @ExceptionHandler({ArithmeticException.class})
        public ModelAndView handleArithmeticException(Exception e){
            System.out.println("出异常了:"+e);
            ModelAndView mv = new ModelAndView("error");
            mv.addObject("exception",e);
            return mv;
        }
    

    然后我们在error.jsp页面上通过${exception}即使显示错误信息了。

    对于优先级的问题,我们此时再定义一个处理异常的方法:

    @ExceptionHandler({RuntimeException.class})
        public ModelAndView handleArithmeticException2(Exception e){
            System.out.println("***出异常了:"+e);
            ModelAndView mv = new ModelAndView("error");
            mv.addObject("exception",e);
            return mv;
        }
    

    此时有两个处理异常的方法,会执行哪一个呢,按照继承关系,是ArithmeticException异常最近,因此会执行这个,如果我们此时把ArithmeticException异常的方法去掉,则会执行RuntimeException异常的方法,如果两个方法都没有则会去查找@ControllerAdvice标记的类中的@ExceptionHandler标记的方法,现在我们先把这两个方法给注释,然后创建一个实体类:HandleException,代码如下:

    package com.cerr.springmvc.handlers;
    
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.servlet.ModelAndView;
    
    @ControllerAdvice
    public class HandleException {
        @ExceptionHandler({ArithmeticException.class})
        public ModelAndView handleArithmeticException(Exception e){
            System.out.println("出异常了:"+e);
            ModelAndView mv = new ModelAndView("error");
            mv.addObject("exception",e);
            return mv;
        }
    }
    

    这样的话,由于我们的方法的类中没有@ExceptionHandler注释的方法,因此它会来找@ControllerAdvice注解的类中的@ExceptionHandler注解的方法,很明显能找到,因此就执行该方法,如果都没有找到那就报错。

    总结而言就是:

    • @ExceptionHandler方法的入参中可以加入Exception类型的参数,该参数即对应发生的异常对象。
    • @ExceptionHandler方法的入参中不能传入Map,若希望把异常信息传到页面上,需要使用ModelAndView作为返回值
    • @ExceptionHandler方法标记的异常有优先级问题
    • 如果在当前handler中找不到@ExceptionHandler方法出现的异常,则去@ControllerAdvice标记的类中查找@ExceptionHandler标记的方法

    ResponseStatusExceptionResolver

    处理@ResponseStatus标注的异常类或异常方法,在异常及异常父类中找到@ResponseStatus注解,然后使用这个注解的属性进行处理。
    @ResponseStatus注解有两个属性,一个是value,表示状态码,而reason表示错误消息。

    在类中使用@ResponseStatus注解

    我们先定义一个异常类:

    package com.cerr.springmvc.test;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    @ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "用户名和密码不匹配")
    public class UserNameNotMatchPasswordException extends RuntimeException{
    
    }
    

    然后我们在控制器类中编写一个方法如下:

        @RequestMapping(value = "/testResponseStatusExceptionResolver")
        public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
            if(i == 13){
                throw new UserNameNotMatchPasswordException();
            }
            System.out.println("testResponseStatusExceptionResolver...");
    
            return "success";
        }
    

    这个方法会接收一个参数为i的整形参数,如果i的值为13,就抛出我们刚刚定义的异常类。我们在浏览器中访问http://localhost:8080/springmvc_test/testResponseStatusExceptionResolver?i=13,结果如下:

    在目标方法中标记@ResponseStatus

        @ResponseStatus(reason = "测试",value = HttpStatus.FORBIDDEN)
        @RequestMapping(value = "/testResponseStatusExceptionResolver")
        public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
            if(i == 13){
                throw new UserNameNotMatchPasswordException();
            }
            System.out.println("testResponseStatusExceptionResolver...");
    
            return "success";
        }
    

    我们在刚刚的目标方法上加入了@ResponseStatus注解,然后我们在浏览器中访问http://localhost:8080/springmvc_test/testResponseStatusExceptionResolver?i=10,此时i不等于13,因此我们自定义的那个异常类不会被触发,但是会触发我们注解上声明的那个异常,结果如下:


    DefaultHandlerExceptionResolver

    对一些特殊的异常进行处理。


    SimpleMappingExceptionResolver

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

    我们现在定义一个目标方法:

        @RequestMapping(value = "/testSimpleMappingExceptionResolver")
        public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){
            String [] val = new String[10];
            System.out.println(val[i]);
            return "success";
        }
    

    该方法接受一个参数i,并访问数组的i下标的内容,我们现在要使其发生数组下标越界异常,我们就要传入i>10的值就行了。

    我们在配置文件中配置SimpleMappingExceptionResolver

        <!-- 配置使用SimpleMappingExceptionResolver来映射异常 -->
        <bean id="simpleMappingExceptionResolver"
              class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <property name="exceptionMappings">
                <props>
                    <!--发生java.lang.ArrayIndexOutOfBoundsException异常时会跳转到error.jsp页面-->
                    <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
                </props>
            </property>
        </bean>
    

    在错误页面中对异常信息的显示,通过观察其源码发现,其会在ModelAndView中将错误信息添加进去,所以在错误页面中我们可以通过${名字}来显示错误页面,默认的名字为exception,因此我们可以通过${exception}来显示。也可以更改这个默认的名字,在<property>节点中通过exceptionAttribute属性来修改其名字,例如下面的配置:

        <!-- 配置使用SimpleMappingExceptionResolver来映射异常 -->
        <bean id="simpleMappingExceptionResolver"
              class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <property name="exceptionAttribute" value="ex" />
            <property name="exceptionMappings">
                <props>
                    <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
                </props>
            </property>
        </bean>
    

    该配置中使用了<property name="exceptionAttribute" value="ex" />,因此我们在页面中要使用${ex}来显示错误信息。

    访问后结果如下:


    error.jsp源码:

    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h4>Error Page</h4>
        ${ex}
    </body>
    </html>
    
    

    相关文章

      网友评论

        本文标题:SpringMVC学习笔记 | 关于异常处理

        本文链接:https://www.haomeiwen.com/subject/ypzgictx.html