SpringMVC通过HandlerExceptionResolver接口来处理程序的异常,包括Handler映射、数据绑定和目标方法执行时发生的异常。
1. HandlerExceptionResolver
HandlerExceptionResolver的具体实现类主要有:
其中,DispatcherServlet会默认装配HandlerExceptionResolver,在SpringMVC配置文件中没有使用mvc:annotation-driven配置时,其自动装配的HandlerExceptionResolver如下:
然而,在开发中通常会在SpringMVC配置文件中使用mvc:annotation-driven配置,其自动装配的HandlerExceptionResolver如下:
2. ExceptionHandlerExceptionResolver
如下Handler类和异常处理类针对算术异常的处理优先级为:TestExceptionHandler的handleArithmeticException()方法 > TestExceptionHandler的handleRuntimeException()方法 > SpringMVCExceptionHandler的handleArithmeticException()方法。
package com.qiaobc.springmvc.exception;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestExceptionHandler {
@ExceptionHandler({RuntimeException.class})
public ModelAndView handleRuntimeException(Exception exception) {
System.out.println("局部异常处理之运行时异常 : " + exception);
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("EXCEPTION", exception);
return modelAndView;
}
/**
* 关于SpringMVC的异常处理问题:
* 1). @ExceptionHandler标记的方法为(局部)异常处理方法,在当前处理器的目标方法发生指定异常时被执行
* 2). @ExceptionHandler标记的方法的入参中可以加入Exception类型的参数,其即为所发生的异常对象
* 3). @ExceptionHandler标记的方法的入参中不能添加Map类型的参数,可使用ModelAndView将异常信息传到目标页面
* 4). @ExceptionHandler标记的方法具有优先级的问题,先进行精确匹配,再进行模糊匹配
* 5). 若当前Handler中没有@ExceptionHandler标记的方法,则发生异常时:
* 查找@ControllerAdvice标记的类中的@ExceptionHandler方法(全局异常处理)
*/
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception exception) {
System.out.println("局部异常处理之算术异常 : " + exception);
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("EXCEPTION", exception);
return modelAndView;
}
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("count") int count) {
System.out.println("Result : " + 10 / count);
return "success";
}
}
package com.qiaobc.springmvc.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class SpringMVCExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception exception) {
System.out.println("全局异常处理之算术异常 : " + exception);
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("EXCEPTION", exception);
return modelAndView;
}
}
3. ResponseStatusExceptionResolver
该异常处理类可以对@ResponseStatus注解进行解析(将异常转化为HTTP状态码),并使用该注解的属性值(HTTP状态码)响应请求页面。
①. @ResponseStatus注解修饰异常类时
package com.qiaobc.springmvc.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPassword extends RuntimeException {
private static final long serialVersionUID = 1L;
}
说明:若在处理器的目标方法中抛出该异常,且ExceptionHandlerExceptionResolver不解析时,由于触发的异常带有@ResponseStatus注解,ResponseStatusExceptionResolver会对该异常进行解析,并响应HttpStatus.FORBIDDEN代码给客户端。
@Controller
public class TestExceptionHandler {
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("status") int status) {
if(status == 99) {
throw new UserNameNotMatchPassword();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
}
②. @ResponseStatus注解修饰目标方法时
由于该方法带有@ResponseStatus注解,故执行该目标方法且status不为99时,ResponseStatusExceptionResolver会对其进行解析,并直接响应HttpStatus.NOT_FOUND代码给客户端;若status为99则会响应HttpStatus.FORBIDDEN代码给客户端。
@Controller
public class TestExceptionHandler {
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="测试")
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("status") int status) {
if(status == 99) {
throw new UserNameNotMatchPassword();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
}
4. DefaultHandlerExceptionResolver
该异常处理类可以对Spring的特殊异常进行处理,并将其转化为对应的HTTP状态码,进而响应为客户端。
/**
* DefaultHandlerExceptionResolver : 主要对Spring的特殊异常进行处理,具体参看源代码
* 请求:<a href="testDefaultHandlerExceptionResolver">Test DefaultHandlerExceptionResolver</a>
* @return
*/
@RequestMapping(value="/testDefaultHandlerExceptionResolver", method=RequestMethod.POST)
public String testDefaultHandlerExceptionResolver() {
return "success";
}
5. SimpleMappingExceptionResolver
如果希望对所有异常进行统一处理,可以使用该异常解析器将异常类名映射为视图名,即发生指定异常时所转向的页面。
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 配置将异常信息放入request请求域的EXCEPTION属性中,若不配置则默认放入exception属性中 -->
<property name="exceptionAttribute" value="EXCEPTION"></property>
<!-- 配置发生指定异常时转向的页面 -->
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
/**
* 使用SimpleMappingExceptionResolver处理java.lang.ArrayIndexOutOfBoundsException
*/
@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(@RequestParam("index") int index) {
int[] arrays = new int[10];
for(int i = 0; i < arrays.length; i++) {
arrays[i] = 3 * i;
}
System.out.println(arrays[index]);
return "success";
}