一.SpringMVC异常处理机制
- springMVC会将所有在doDispatch方法中的异常捕获,然后处理。无法处理的异常会抛出给容器处理。
在doDispatch()中调用processDispatchResult - 如果有异常出现,首先判断是不是ModelAndViewDefiningException,这个异常是人为的定义的,指定了modelAndView,如果不是ModelAndViewDefiningException,则要调用processHandlerException(request, response, handler, exception),该方法会遍历所有的handlerExceptionResolvers,判断能否处理该异常。调用各自的resolveException 方法(一般抛出的异常都需要通过这一步,要么能被处理;要么处理不了,向上抛)
二.web.xml配置错误页面
1.按错误号指定跳转(404,500,…)
(1) 使用error-page标签的要点
- 如果是JSP中抛出的异常,容器首先查看JSP中有没有指定errorPage属性,如果没有,再查看web.xml中有没有error-page标签;如果是Web应用中的其它地方抛出异常,容器只会查看web.xml中的error-page标签。
- 不论是根据exception-type还是error-code来声明错误页面,location指定的路径必须以斜线开头,也就是说它是相对于Web应用根的。
- 一个web.xml中可以使用多个error-page标签,一个error-page标签中只能使用一个exception-type或error-page。
(2) 在web.xml新增配置信息
根据错误编码指定页面
<error-page>
<error-code>404</error-code>
<location>/error/404</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500</location>
</error-page>
定义404,500页面控制器
/**
* 错误:404
*/
@RequestMapping(value = "/404")
@ResponseStatus(HttpStatus.NOT_FOUND)
public String error404() {
return "404";
}
/**
* 错误:500
*/
@RequestMapping(value = "/500")
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String error500() {
return "505";
}
2.按异常类型指定跳转
在web.xml添加error-page,根据异常类型,添加指定的页面
<!--根据错误类型指定页面-->
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/error/null</location>
</error-page>
定义null接口
/**
* 错误:空指针异常
*/
@LoginAuth(validate = false)
@RequestMapping(value = "/null")
public String error_null() {
return "null";
}
测试用例
/**
* @Description 测试空指针异常
**/
@RequestMapping("/test_null")
public String test_null(){
int c=1/0;
return "/login";
}
三.异常统一处理的三种方式
1.使用 @ ExceptionHandler 注解
使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。使用如下:
@Controller
public class CustomExceptionController {
/**
* 用于处理自定义异常
* @return
*/
@ExceptionHandler({CustomException.class})
@ResponseBody
public TResult exception(CustomException e) {
return TResultUtils.error(e.getMessage());
}
@RequestMapping("test_error")
@ResponseBody
public void test() {
throw new CustomException("出错了!");
}
}
2.使用 @ControllerAdvice+ @ ExceptionHandler 注解
@ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面,当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。
1.示例
自定义异常信息
public class CustomException extends RuntimeException {
private String message;
public CustomException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
接口处理异常
@Override
public TResult login(User user) {
//参数验证
if(user.getUsername()==null||user.getUsername().equals("")
||user.getPassword()==null||user.getPassword().equals("") ){
throw new CustomException("请输入完整");
}
//身份验证
if(user.getUsername().equals("admin")&&user.getPassword().equals("admin")){
request.getSession().setAttribute("user",user);
return TResultUtils.success();
}
throw new CustomException("请检查您的用户名或者密码是否正确");
}
全局异常信息处理
@ControllerAdvice
@ResponseBody
public class AllControllerAdvice {
/**
* 用于处理自定义异常
* @return
*/
@ExceptionHandler({CustomException.class})
public TResult exception(CustomException e) {
return TResultUtils.error(e.getMessage());
}
/**
* @Description 400 - Bad Request
**/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public TResult handleBadRequst(Exception e) {
return TResultUtils.error(e.getMessage());
}
/**
* @Description 405 - Method Not Allowed
**/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public TResult handleMethodNotAllowed(Exception e) {
return TResultUtils.error(e.getMessage());
}
/**
* @Description 415 - Unsupported Media Type
**/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public TResult handleUnsupportedMediaType(Exception e) {
return TResultUtils.error(e.getMessage());
}
/**
* @Description 500
**/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public TResult handleIOException(Exception e) {
return TResultUtils.error(e.getMessage());
}
}
如果 @ExceptionHandler 注解中未声明要处理的异常类型,则默认为参数列表中的异常类型。所以还可以写成这样:
@ExceptionHandler()
@ResponseBody
TResult handleException(Exception e){
return TResultUtils.error(e.getMessage());
}
3.实现 HandlerExceptionResolver 接口
上面的异常处理方法是json信息,如果希望返回跳转页面,则需要实现HandlerExceptionResolver类来进行异常处理并跳转,下面定义HandleMvException ,实现HandleMvException 接口
public class HandleMvException implements HandleMvException {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
CustomException customException = null;
if (ex instanceof CustomException) {
customException = (CustomException)ex;
}else{
customException = new CustomException("未知错误!");
}
String errMsg = customException.getMessage();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", errMsg);
modelAndView.setViewName("error");
return modelAndView;
}
}
测试用例
/**
* @Description 测试异常
**/
@RequestMapping("/test_error_msg")
public String test_error_msg() {
int c=0;
if(c==0){
throw new CustomException("空指针异常...");
}
return "/login";
}
访问 /test_error_msg 则会进入resolveException方法进行处理,然后直接进入error页面