一、Spring MVC异常处理概述
Spring MVC 通过 HandlerExceptionResolver(接口)处理程序的异常,HandlerExceptionResolver接口的实现类:
对应于这几个实现类,Spring MVC提供了多种方式将异常转换为响应:
- 默认的异常响应
- 将异常映射为指定的HTTP状态码
- 在控制器类中编写异常响应方法
- 在控制器通知类中集中响应异常
- 在配置文件中配置异常响应
一、默认的异常响应
对于一些异常:
Spring MVC默认会将这些异常交给DefaultHandlerExceptionResolver处理,映射为HTTP状态吗。
示例:
@RequestMapping(value="/testDefaultHandlerExceptionResolver", method=RequestMethod.POST)
public String testDefaultHandlerExceptionResolver() {
System.out.println("testDefaultHandlerExceptionResolver");
return "success";
}
使用GET方式访问这个映射,就会默认映射为HTTP状态码405:
二、将异常映射为指定的HTTP状态码
对于上面所说的将异常映射为默认的HTTP状态码,Spring MVC也可以通过ResponseStatusExceptionResolver类将异常映射为我们指定的HTTP状态码。
首先需要编写一个自定义异常类,例如禁止某个参数的异常类:
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="This Argument is forbidden!")
public class ForbiddenArgumentException extends RuntimeException{
}
上述异常会被映射为value中指定的HttpStatus.FORBIDDEN状态,reason指定了异常信息。
在控制器类的方法中使用这个异常:
@RequestMapping(value="/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if(i == 0){
throw new ForbiddenArgumentException();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
当使用参数i=0访问该URL时,会触发ForbiddenArgumentException异常,并被映射为HTTP状态码403:
三、在控制器类中编写异常响应方法
有时候需要在控制器类的处理请求方法中编写try..catch异常处理代码,这通常会使增加该方法内的代码和逻辑。为了将处理请求方法中的异常处理代码剥离,我们可以在控制器类中编写异常响应方法,在该方法中处理异常。
比如下面这个处理请求方法如果接收的参数为0时,会触发java.lang.ArithmeticException异常:
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
System.out.println("10/" + i + "=" + (10/i));
return "success";
}
可以在控制器类中编写异常响应方法处理异常:
@ExceptionHandler(value={java.lang.ArithmeticException.class})
public ModelAndView handleException(Exception ex){
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
这里有几点需要注意:
- 如果有多个异常响应方法,则按异常类型匹配程度,顺序执行。
- 异常对象(上面的Exception ex)不能通过Map集合方式传递给视图,只能通过ModelAndView传递。
四、在控制器通知类中集中响应异常
如果多个控制器类中都会抛出某个特定的异常,那么就需要在所有的控制器方法中重复相同的@ExceptionHandler方法。我们可以编写控制器通知类集中响应异常。
比如可以编写一个控制器通知类响应上面的java.lang.ArithmeticException异常:
@ControllerAdvice
public class ExceptionAdviceHandler {
@ExceptionHandler(value={java.lang.ArithmeticException.class})
public ModelAndView handleException(Exception ex){
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex); // 将异常对象传递给视图
return mv;
}
}
如果控制器类中有@ExceptionHandler方法,那么控制器方法触发异常时,本类中的@ExceptionHandler方法会响应,而控制器通知类中的@ExceptionHandler方法不会响应。所以总结起来就是控制器类中的@ExceptionHandler方法总是会屏蔽控制器通知类中的@ExceptionHandler方法。
五、在配置文件中配置异常响应
如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。这些上面的几种方式都能实现,但是这种方式是通过配置Spring的配置文件实现的。
这里就不介绍这种方法了。