- Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
- SpringMVC 提供的 HandlerExceptionResolver 的实现类
箭头所指为常用的实现类
DispatcherServlet 默认装配的 HandlerExceptionResolver :
- 没有使用
<mvc:annotation-driven/>
配置:
- 使用了
<mvc:annotation-driven/>
配置:
1. ExceptionHandlerExceptionResolver
作用:主要处理 Handler(Controller) 中用 @ExceptionHandler
注解定义的方法。
位置:方法上
Controller定义一个目标方法
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
System.out.println("result: " + (10 / i));
return "success";
}
index.jsp
<a href="testExceptionHandlerExceptionResolver?i=10">Test ExceptionHandlerExceptionResolver</a>
还需要再Controller定义一个处理异常的方法
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(Exception ex){
System.out.println("出异常了: " + ex);
return "error";
}
再view中创建一个错误页面 error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Error Page</h4>
</body>
</html>
结果:
1.1 异常打印到页面的细节问题
我们如何将异常带到页面上呢?
常用的方法时在入参中使用Map,如下:
@ExceptionHandler({ArithmeticException.class})
public String handleArithmeticException(Exception ex, Map<String, Object> map){
System.out.println("出异常了: " + ex);
map.put("exception", ex)
return "error";
}
结果会报出500错误
细节:使用返回值为ModelAndView
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
error.jsp页面
<h4>Error Page</h4>
${exception}
结果:
1.2 异常优先级的细节问题
在Controller编写两个异常处理方法
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
@ExceptionHandler({RuntimeException.class})
public ModelAndView handleArithmeticException2(Exception ex){
System.out.println("[出异常了]: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
结果:
可以看出,ArithmeticException异常方法起作用,异常优先级是精度匹配的,匹配度高的异常抛出,以上两个方法ArithmeticException匹配度更高
1.3 全局异常处理
上面个两个方法只能处理该Handler(Controller)中方法的异常,如果要处理全局的异常,我们需要创建一个SpringMVCTestExceptionHandler类
package com.springmvc.test;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class SpringMVCTestExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("----> 出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
}
把原先两个异常处理方法去掉
运行得到结果:
1.3.1 关于@ControllerAdvice
如果在当前 Handler 中找不到 @ExceptionHandler
方法来出来当前方法出现的异常,则将去 @ControllerAdvice
标记的类中查找 @ExceptionHandler
标记的方法来处理异常
1.4 细节点总结:
- 在
@ExceptionHandler
方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象 @ExceptionHandler
方法的入参中不能传入 Map,若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值@ExceptionHandler
方法标记的异常有优先级的问题@ControllerAdvice
:如果在当前 Handler(Controller) 中找不到@ExceptionHandler
方法来出来当前方法出现的异常,则将去@ControllerAdvice
标记的类中查找@ExceptionHandler
标记的方法来处理异常
2. ResponseStatusExceptionResolver
- 在异常及异常父类中找到
@ResponseStatus
注解,然后使用这个注解的属性进行处理。 - 位置:类上、方法上
创建一个异常类
package com.springmvc.test;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
public class UserNameNotMatchPasswordException extends RuntimeException{
private static final long serialVersionUID = 1L;
}
Controller中的目标方法
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if(i == 13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
index.jsp
<a href="testResponseStatusExceptionResolver?i=13">Test ResponseStatusExceptionResolver</a>
当i=13时,会报出我们定义的错误类
2.1 用@ResponseStatus
注解修饰异常类(定制状态码和错误信息)
package com.springmvc.test;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
}
点击index.jsp的ResponseStatusExceptionResolver超链接
结果如下:
2.2 用@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";
}
这时我们点击超链接
<a href="testResponseStatusExceptionResolver?i=10">Test ResponseStatusExceptionResolver</a>
结果如下:
@ResponseStatus
注解标注于方法时,无论方法有没有正常执行,他还是会返回定制页面,我们可以通过后台知道,方法正常执行了
3. DefaultHandlerExceptionResolver
作用:对SpringMVC的一些特殊异常进行处理
可以处理的异常类有
NoSuchRequestHandlingMethodException |
HttpRequestMethodNotSupportedException |
HttpMediaTypeNotSupportedException |
HttpMediaTypeNotAcceptableException |
MissingServletRequestParameterException |
ServletRequestBindingException |
ConversionNotSupportedException |
TypeMismatchException |
HttpMessageNotReadableException |
HttpMessageNotWritableException |
MethodArgumentNotValidException |
MissingServletRequestPartException |
BindException |
NoHandlerFoundException |
Controller目标方法
@RequestMapping(value="/testDefaultHandlerExceptionResolver",method=RequestMethod.POST)
public String testDefaultHandlerExceptionResolver(){
System.out.println("testDefaultHandlerExceptionResolver...");
return "success";
}
index.jsp
<a href="/testDefaultHandlerExceptionResolver">Test DefaultHandlerExceptionResolver</a>
结果
4. SimpleMappingExceptionResolver
- 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
Controller目标方法
@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){
String [] vals = new String[10];
System.out.println(vals[i]);
return "success";
}
SpringMVC配置文件配置SimpleMappingExceptionResolver
<!-- 配置使用 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><!-- 页面转向error页面 -->
</props>
</property>
</bean>
index.jsp
<a href="testSimpleMappingExceptionResolver?i=21">Test SimpleMappingExceptionResolver</a>
结果:
error.jsp
使用SimpleMappingExceptionResolver会自己把异常信息转到页面上
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Error Page</h4>
${requestScope.ex }
</body>
</html>
注意:${requestScope.ex }
中的ex对应配置文件中的<property name="exceptionAttribute" value="ex"></property>
的ex,默认值为exception