SpringMVC中的异常处理

来源:互联网 发布:淘宝卖空军一号真的吗 编辑:程序博客网 时间:2024/05/22 06:55

  Spring MVC通过HandlerExceptionResolver处理程序的异常,包括Handler 映射、数据绑定以及目标方法执行时发生的异常。在Eclipse中可以查看到HandleExceptionResolver接口的实现类:
  
  image_1b1gosocup6i1ugoq3p119qdta9.png-34.4kB

  如果我们没有在springmvc配置文件中配置<mvc:annotation-driven/>,那么DispatcherServlet会默认装配AnnotationMethodHandlerExceptionResolver、DefaultHandlerExceptionResolver和ResponseStatusException三种解析器,但是其中AnnotationMethodHandlerExceptionResolver已经过期了。所以我们配置<mvc:annotation-driven/>,将会把AnnotationMethodHandlerExceptionResolver替换为ExceptionHandlerExceptionResolver。
  下面的例子中,我们都默认已经在spring配置文件中配置了<mvc:annotation-driven/>。

ExceptionHandlerExceptionResolver

  ExceptionHandlerExceptionResolver主要用于处理Handler 中用@ExceptionHandler注解标记的方法,示例如下:


页面
index.jsp

<a href="testExceptionHandlerExceptionResolver?i=0">Test ExceptionHandlerExceptionResolver</a>

error.jsp

<h4>Error Page</h4>${exception }

控制器

private final static String ERROR = "error";    //异常处理器    //value = ArithmeticException.class声明捕获数学异常    @ExceptionHandler(value = ArithmeticException.class)     public ModelAndView handleArithmeticException(Exception ex) {        ModelAndView mv = new ModelAndView(ERROR);        System.out.println("出异常了:" + ex);        mv.addObject("exception", ex);        return mv;    }    //定义一个可以抛出ArithmeticException异常的方法    @RequestMapping("/testExceptionHandlerExceptionResolver")    public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i) {        System.out.println("result:" + 10 / i);        return SUCCESS;    }

运行程序:
点击index.jsp中的超链接,异常处理器会捕获到异常,在控制台输出:

image_1b1gqo9gavuv47c1s4lq8r1je9m.png-13.7kB

并跳转到error.jsp,且在该页面打印了异常信息:

image_1b1gqpbg41slr1e5dccbvk91oju13.png-12.6kB


需要注意的是:

  1. @ExceptionHandler标记的方法的入参中不能传入Map。 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值。
  2. @ExceptionHandler 方法标记的异常有优先级的问题。如果我们在上面的例子中同时声明了可以捕获ArithmeticException异常和RuntimeException异常的两个异常处理器,那么会使用匹配度更高的,即捕获ArithmeticException异常的异常处理器。
  3. 通过以上方式创建的异常处理器只能处理该控制器类内部的异常,若需要统一配置可以捕获所有控制器类抛出的异常,可以新建一个异常处理器类,在类的声明处用@ControllerAdvice注解标记即可。如果在当前 控制器类 中找不到 @ExceptionHandler 方法来处理当前方法出现的异常, 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常。例如,我们现在创建一个异常处理器类,并在其中定义一个可以处理数学异常的方法:
@ControllerAdvicepublic class SpringMVCTestExceptionHandler {    private static final String ERROR = "error";    @ExceptionHandler(value=ArithmeticException.class)    public ModelAndView handleArithmeticException1(Exception ex){        ModelAndView mv = new ModelAndView(ERROR);        System.out.println("[出异常了]:"+ex);        mv.addObject("exception",ex);        return mv;    }}

注释掉之前定义的handleArithmeticException方法,运行程序,与之前的例子类似,仍然跳转到error.jsp,但是在控制台输出:

image_1b1hdanpd1bqcb3sdc215gporm1g.png-26.6kB

表示执行的是我们新定义的handleArithmeticException1方法,注意,如果不注释掉handleArithmeticException方法,那么会优先执行handleArithmeticException方法,因为handleArithmeticException方法和抛出异常的testExceptionHandlerExceptionResolver方法定义在同一个控制器类中,testExceptionHandlerExceptionResolver方法抛出异常后会先在当前 控制器类 中找 @ExceptionHandler方法来处理当前方法出现的异常,如果找不到,才会去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常。

ResponseStatusExceptionResolver

  ResponseStatusExceptionResolver的作用是在异常及异常父类中找到@ResponseStatus注解,然后使用这个注解的属性进行处理。下面举例说明:


页面

<a href="testResponseStatusExceptionResolver?i=13">Test ResponseStatusExceptionResolver</a>

定义一个异常类

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名和密码不匹配")public class UserNameNotMatchPasswordException extends RuntimeException{    /**     *      */    private static final long serialVersionUID = 1L;}

模拟一个可以抛出上述异常的方法

    //定义一个可以抛出UserNameNotMatchPasswordException异常的方法        @RequestMapping("/testResponseStatusExceptionResolver")        public String testResponseStatusExceptionResolver(@RequestParam("i") int i) {            if(i==13)                throw new UserNameNotMatchPasswordException();            return SUCCESS;        }

运行程序,由于抛出了异常,并且我们没有定义任何异常处理器对该异常进行处理,那么会出现我们定制的异常页面:

image_1b1hfd8r917su1p7i1viie55ac1t.png-28.8kB

此外,@ResponseStatus注解还可以用来标记方法:

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

现在,我们在浏览器中令i=10,这样方法是不会抛出UserNameNotMatchPasswordException异常的,结果程序跳转到了如下页面:

image_1b1hg7ludgi3dqeadvea917mq2a.png-23.5kB

但是控制台上输出了如下信息,说明这个方法被正常地执行了:

image_1b1hg8ssm5goidn1f5m1rjqjm42n.png-27.9kB

DefaultHandlerExceptionResolver

  对一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException等,具体处理哪些异常可以在DefaultHandlerExceptionResolver源码中查看doResolveException()方法。

SimpleMappingExceptionResolver

  SimpleMappingExceptionResolver可以将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。下面进行测试:


页面

index.jsp

<a href="testSimpleMappingExceptionResolver?i=21">Test SimpleMappingExceptionResolver</a>

error.jsp

<h4>Error Page</h4>${exception }

控制器

// 定义一个可以抛出ArrayIndexOutOfBoundsException异常的方法    @RequestMapping("/testSimpleMappingExceptionResolver")    public String testSimpleMappingExceptionResolver(@RequestParam("i") int i) {        int[] values = new int[20];        System.out.println(values[i]);        return SUCCESS;    }

spring配置文件

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

运行程序:

image_1b1hi0jq9bnd76gji918aa1pe234.png-14.7kB

由于目标方法抛出了数组下标越界异常,程序跳转到了error.jsp页面,并且在页面上打印了异常信息,这是因为在SimpleMappingExceptionResolver中有一个String类型的属性exceptionAttribute,其默认值为exception,所以我们在error.jsp中通过${exception }可以获取到。也可以在配置文件中配置exceptionAttribute的值,例如:

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

那么,此时需要在error.jsp中通过如下方式在可以获取到异常信息:

<h4>Error Page</h4>${ex }
0 0