SpringMVC提供了HandlerExceptionResolver接口来处理异常,并在web容器初始化时被dispatcherServlet自动加载。
这篇文章说一下ExceptionHandlerExceptionResolver
、ResponseStatusExceptionResolver
、DefaultHandlerExceptionResolver
、SimpleMappingExceptionResolver
等四种实现类处理异常的方式。
1.ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver
是HandlerExceptionResolver
接口的一个实现类,该类提供了@ExceptionHandler
注解用来捕获指定类型的异常。我们下面通过一个示例来了解一下ExceptionHandlerExceptionResolver处理异常的基本流程:
请求处理类(产生一个异常):ExceptionHandlerExceptionResolverDemo.java
@Controller
@RequestMapping("ExceptionHandlerExceptionResolverDemo")
public class ExceptionHandlerExceptionResolverDemo {
@RequestMapping("testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver() {
//产生一个ArithmeticException异常
int num = 1/0;
return "success";
}
//捕获本类中所有方法抛出的ArithmeticException异常
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(Exception ex) {
System.out.println("异常:"+ex);
return "error";
}
}
index.jsp
<a href="ExceptionHandlerExceptionResolverDemo/testExceptionHandlerExceptionResolver">test</a>
执行index.jsp中的超链接,可以发现控制台输出了handleArithmeticException()方法中的异常提示,并且页面跳转到了error.jsp,如图。
可以发现,@ExceptionHandler({ArithmeticException.class})
标识的方法会捕获当前类中抛出的ArithmeticException类型的异常。也就是说,在某一个类中产生的异常,可以通过在该类中定义一个被@ExceptionHandler({异常类型.class})标识的方法来捕获此异常。并且,异常对象会被保存在Exception类型的参数中。
我们知道ArithmeticException
是RuntimeException
的子类,如果在一个类中抛出一个ArithmeticException类型的异常,而该类中既存在@ExceptionHandler({ArithmeticException.class})修饰的方法,又存在@ExceptionHandler({RuntimeException.class})修饰的方法,那么异常只会被@ExceptionHandler({ArithmeticException.class})修饰的方法所捕获。只有当本类中不存在@ExceptionHandler({ArithmeticException.class})修饰的方法时,此异常才会被@ExceptionHandler({RuntimeException.class})修饰的方法所捕获。也就是说,如果有多个@ExceptionHandler修饰的方法可以捕获同一个异常时,异常只会被最精确的处理方法所捕获。
还需要注意的是,使用@ExceptionHandler修饰的方法的参数,只能是Throwable或其子类类型,并且不能再有其他参数存在
例如可以写成以下几种形式:
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(Throwable ex)
{…}
@ExceptionHandler({ArithmeticException.class})
public String handleException(Exception ex)
{…}
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(ArithmeticException aEx)
{…}
而不能写成:
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(Exception ex,Map<String,Object> map)
{…}
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(String message)
{…}
已经知道,使用@ExceptionHandler
修饰的方法可以捕获同一个类中的异常。但如果@ExceptionHandler
修饰的方法与产生异常的方法不在同一个类之中,那么产生的异常就不会被捕获。为此,我们可以这样解决:
新建一个类,并且使用@ControllerAdvice
注解修饰此类,如下:
MyExceptionHandler.java
@ControllerAdvice
public class MyExceptionHandler{
@ExceptionHandler({ArithmeticException.class})
public ModelAndView MyException(Exception e){
System.out.println("(异常处理类)异常信息:"+e);
ModelAndView mv = new ModelAndView("exception");
mv.addObject("ex", e);
return mv;
}
}
@ControllerAdvice
修饰的类可以理解为“异常处理类”,此异常处理类就可以捕获项目中所有类中方法产生的ArithmeticException异常。
如果同一个异常既可以被同一个类中@ExceptionHandler修饰的方法所捕获,又可以被异常处理类中@ExceptionHandler修饰的方法所捕获,那么根据“就近原则”只会被同一个类中@ExceptionHandler修饰的方法所捕获。
2.ResponseStatusExceptionResolver
在开发中,我们经常会看到类似的异常页面,
页面中的异常信息都是服务器预先设置好的,除此之外,我们还可以自己定制这些页面中的异常信息:
通过ResponseStatusExceptionResolver
类提供的@ResponseStatus
注解来实现
通过@ResponseStatus注解的value属性指定异常的状态码(如404等)、reason属性指定具体的异常信息,如下
其中HttpStatus.FORBIDDEN的值是403。
ResponseStatusDemo.java
@Controller
@RequestMapping("ResponseStatusDemo")
public class ResponseStatusDemo {
@RequestMapping("testResponseStatus")
public String testResponseStatus(@RequestParam Integer i) {
if(i==10) {
throw new MyArrayOutOfBounderException();
}
return "success";
}
}
index.jsp
<a href="ResponseStatusDemo/testResponseStatus?i=10">testResponseStatus</a>
执行index.jsp中的超链接,运行结果:
@ResponseStatus注解除了能标识在异常类上以外,还可以标识在方法上。@ResponseStatus标识在方法上时,就会使访问此方法的请求直接显示@ResponseStatus指定的异常信息,如下:
ResponseStatusDemo.java
@Controller
@RequestMapping(value = "/SecondSpringDemo")
public class ResponseStatusDemo{
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="我在测试forbidden")
@RequestMapping("/testResponseStatusWithMethod")
public String testResponseStatusWithMethod(){
return "sucess";
}
}
index.jsp
<a href="ResponseStatusDemo/testResponseStatusWithMethod">testResponseStatusWithMethod</a>
执行index.jsp中的超链接,去访问被@ResponseStatus注解标识的方法,就能直接得到异常页面。
3.DefaultHandlerExceptionResolver
Springmvc默认装配了DefaultHandlerExceptionResolver
异常解析器 ,会对一些特殊的异常,如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等进行处理。以下,我们以NoSuchRequestHandlingMethodException为例进行测试:
如果某一个请求的请求方式,与请求处理方法所指定的请求方式不一致,就会抛出NoSuchRequestHandlingMethodException异常。例如,请求的是GET方式,而请求处理方法指定的是POST方式,如下:
NoSuchRequestHandlingMethodExceptionDemo.java
@Controller
@RequestMapping(value = "/NoSuchRequestHandlingMethodExceptionDemo")
public class NoSuchRequestHandlingMethodExceptionDemo{
@RequestMapping(value="/testNoSuchRequestHandlingMethodException",method=RequestMethod.POST)
public String testNoSuchRequestHandlingMethodException(){
return "success";
}
}
index.jsp
<a href="NoSuchRequestHandlingMethodExceptionDemo/testNoSuchRequestHandlingMethodException">
testNoSuchRequestHandlingMethodException
</a>
执行index.jsp中的超链接,所产生的NoSuchRequestHandlingMethodException异常就会被DefaultHandlerExceptionResolver所捕获并处理。
4.SimpleMappingExceptionResolver
SpringMVC还提供了SimpleMappingExceptionResolver,来让我们通过配置的方式处理异常。例如,先通过以下代码产生一个NumberFormatException异常
@Controller
@RequestMapping(value = "/SecondSpringDemo")
public class SecondSpringDemo{
@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver()
{
//java.lang.NumberFormatException
int num = Integer.parseInt("abc");
return "success" ;
}
}
index.jsp
<a href="SecondSpringDemo/testSimpleMappingExceptionResolver">
testSimpleMappingExceptionResolver
</a>
之后就可以通过在springmvc.xml配置SimpleMappingExceptionResolver,来使得程序在发生NumberFormatException异常后,能跳转到一个指定的错误页面,如下:
springmvc.xml
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="ex"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.NumberFormatException">
error
</prop>
</props>
</property>
</bean>
以上,通过exceptionMappings
属性指定捕获的异常类型是NumberFormatException,并且指定发生此类型的异常后,页面立即跳转到/views/error.jsp页面。并通过exceptionAttribute属性指定将异常对象保存在request作用域中的ex变量之中。
说明:
exceptionAttribute属性的默认值是exception
。即如果不设置exceptionAttribute的值,SpringMVC就会自动的将异常对象保存在request作用域中的exception变量之中。