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

目录

 

一、概述

二、ExceptionHandlerExceptionResolver

三、ResponseStatusExceptionResolver

四、DefaultHandlerExceptionResolver

五、SimpleMappingExceptionResolver


一、概述

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

  • 没有使用<mvc:annotation-driven />配置
AnnotationMethodHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
  • 使用了<mvc:annotation-driven />配置
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
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,会出错:

 
13424350-315e1605f2f6bc99.png
 

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

    @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,结果如下:

 
13424350-2114035b50bce22d.png
 

在目标方法中标记@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,因此我们自定义的那个异常类不会被触发,但是会触发我们注解上声明的那个异常,结果如下:

 
13424350-5e52cec49d35e369.png
 

四、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}来显示错误信息。

访问后结果如下:

 
13424350-addddc8f9ce2622e.png
 

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>

猜你喜欢

转载自blog.csdn.net/qq_14810195/article/details/103161208