-
异常处理概述
- Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
- SpringMVC 提供的 HandlerExceptionResolver 的实现类
- HandlerExceptionResolver
DispatcherServlet 默认装配的 HandlerExceptionResolver :
- 没有使用 <mvc:annotation-driven/> 配置:
- 使用了 <mvc:annotation-driven/> 配置:
- 实验代码
- 页面链接
<a href="testExceptionHandlerExceptionResolver?i=1">testExceptionHandlerExceptionResolver</a> |
- 控制器方法
package com.atguigu.springmvc.crud.handlers;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam;
@Controller public class ExceptionHandler { @RequestMapping("/testExceptionHandlerExceptionResolver") public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){ System.out.println("10/"+i+"="+(10/i)); return "success"; } } |
- 测试,出现异常情况
处理异常,跳转到error.jsp
- 在控制器中增加处理异常的方法
@ExceptionHandler(value={java.lang.ArithmeticException.class}) public String handleException(Exception ex){ System.out.println("出现异常啦L"+ex); return "error"; } |
- 增加error.jsp
<h3>Error Page</h3> |
-
如何将异常对象从控制器携带给页面,做异常信息的获取
- 异常对象不能通过Map集合方式传递给成功页面。(Map不可以)
// @ExceptionHandler(value={java.lang.ArithmeticException.class}) public String handleException(Exception ex,Map<String,Object> map){ System.out.println("出现异常啦:"+ex); map.put("exception",ex); return "error"; } |
- 可以通过ModelAndView将异常对象传递给成功页面上
@ExceptionHandler(value={java.lang.ArithmeticException.class}) public ModelAndView handleException(Exception ex){ System.out.println("出现异常啦:"+ex); ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex); return mv; } |
-
匹配异常类型,执行顺序问题
@ExceptionHandler(value={java.lang.ArithmeticException.class}) public ModelAndView handleException(Exception ex){ System.out.println("出现异常啦:"+ex); ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex); return mv; }
@ExceptionHandler(value={java.lang.RuntimeException.class}) public ModelAndView handleException2(Exception ex){ System.out.println("RuntimeException-出现异常啦:"+ex); ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex); return mv; } |
-
公共的处理异常的类@ControllerAdvice
- 定义公共的处理异常的类
package com.atguigu.springmvc.exceptionAdvice;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice public class ExceptionAdviceHandler {
/* @ExceptionHandler(value={java.lang.ArithmeticException.class}) public ModelAndView handleException(Exception ex){
System.out.println("出现异常啦:"+ex); ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex);
return mv; }*/
@ExceptionHandler(value={java.lang.RuntimeException.class}) public ModelAndView handleException2(Exception ex){
System.out.println("RuntimeException-出现异常啦:"+ex); ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex);
return mv; } } |
-
ExceptionHandlerExceptionResolver
- 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
- @ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有 RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler 注解方法,即标记了 RuntimeException 的方法
- ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice 中的@ExceptionHandler 注解方法
-
异常处理_ResponseStatusExceptionResolver
- 在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
- 定义一个 @ResponseStatus 注解修饰的异常类
- 若在处理器方法中抛出了上述异常:若ExceptionHandlerExceptionResolver 不解析上述异常。由于触发的异常 UnauthorizedException 带有@ResponseStatus 注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.UNAUTHORIZED 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。 关于其他的响应码请参考 HttpStatus 枚举类型源码。
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver Implementation of the HandlerExceptionResolver interface that uses the @ResponseStatus annotation to map exceptions to HTTP status codes. This exception resolver is enabled by default in the org.springframework.web.servlet.DispatcherServlet. |
-
实验代码
- 页面链接
<a href="testResponseStatusExceptionResolver?i=10">testResponseStatusExceptionResolver</a> |
- 自定义异常类
package com.atguigu.springmvc.exception;
import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus;
/** * 自定义异常类 HttpStatus.FORBIDDEN 不允许的,禁用的 */ @ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配") public class UsernameNotMatchPasswordException extends RuntimeException{} |
- 控制器方法
@RequestMapping(value="/testResponseStatusExceptionResolver") public String testResponseStatusExceptionResolver(@RequestParam("i") int i){ if(i==13){ throw new UsernameNotMatchPasswordException(); } System.out.println("testResponseStatusExceptionResolver..."); return "success"; } |
- 出现的错误消息
- 没使用注解时:@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")
- 使用注解时:@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")
- 测试在方法上使用注解
@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="测试方法上设置响应状态码") @RequestMapping(value="/testResponseStatusExceptionResolver") public String testResponseStatusExceptionResolver(@RequestParam("i") int i){ if(i==13){ throw new UsernameNotMatchPasswordException(); } System.out.println("testResponseStatusExceptionResolver..."); return "success"; } |
-
ResponseStatus
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
import org.springframework.http.HttpStatus;
/** * Marks a method or exception class with the status code and reason that should be returned. The status code is applied * to the HTTP response when the handler method is invoked, or whenever said exception is thrown. * * @author Arjen Poutsma * @see org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver * @since 3.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ResponseStatus {
HttpStatus value();
String reason() default "";
} |
- HttpStatus
-
异常处理_DefaultHandlerExceptionResolver
- 对一些特殊的异常进行处理,比如:
- NoSuchRequestHandlingMethodException、
- HttpRequestMethodNotSupportedException、
- HttpMediaTypeNotSupportedException、
- HttpMediaTypeNotAcceptableException等。
- javadoc
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver Default implementation of the HandlerExceptionResolver interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes. This exception resolver is enabled by default in the org.springframework.web.servlet.DispatcherServlet. |
-
实验代码
- 增加页面链接:GET请求
<a href="testDefaultHandlerExceptionResolver">testDefaultHandlerExceptionResolver</a> |
增加处理器方法
//@RequestMapping(value="/testDefaultHandlerExceptionResolver") @RequestMapping(value="/testDefaultHandlerExceptionResolver",method=RequestMethod.POST) //不支持GET请求 public String testDefaultHandlerExceptionResolver(){ System.out.println("testDefaultHandlerExceptionResolver..."); return "success"; } |
- 出现异常错误
- 出现异常交给DefaultHandlerExceptionResolver处理
@Override protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try { if (ex instanceof NoSuchRequestHandlingMethodException) { return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response, handler); } else if (ex instanceof HttpRequestMethodNotSupportedException) { return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request, response, handler); } else if (ex instanceof HttpMediaTypeNotSupportedException) { return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response, handler); } else if (ex instanceof HttpMediaTypeNotAcceptableException) { return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response, handler); } else if (ex instanceof MissingServletRequestParameterException) { return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request, response, handler); } else if (ex instanceof ServletRequestBindingException) { return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response, handler); } else if (ex instanceof ConversionNotSupportedException) { return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler); } else if (ex instanceof TypeMismatchException) { return handleTypeMismatch((TypeMismatchException) ex, request, response, handler); } else if (ex instanceof HttpMessageNotReadableException) { return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler); } else if (ex instanceof HttpMessageNotWritableException) { return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler); } else if (ex instanceof MethodArgumentNotValidException) { return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler); } else if (ex instanceof MissingServletRequestPartException) { return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler); } else if (ex instanceof BindException) { return handleBindException((BindException) ex, request, response, handler); } else if (ex instanceof NoHandlerFoundException) { return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler); } } catch (Exception handlerException) { logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException); } return null; } |
-
异常处理_SimpleMappingExceptionResolver
- 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
- 实验代码
- 增加页面链接
<a href="testSimpleMappingExceptionResolver?i=1">testSimpleMappingExceptionResolver</a> |
- 增加控制器方法
@RequestMapping("/testSimpleMappingExceptionResolver") public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){ System.out.println("testSimpleMappingExceptionResolver..."); String[] s = new String[10]; System.out.println(s[i]); return "success"; } |
- 出现异常情况:参数i的值大于10
- 配置异常解析器:自动将异常对象信息,存放到request范围内
<!-- 配置SimpleMappingExceptionResolver异常解析器 --> <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- exceptionAttribute默认值(通过ModelAndView传递给页面): exception -> ${requestScope.exception} public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception"; --> <property name="exceptionAttribute" value="exception"></property> <property name="exceptionMappings"> <props> <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop> </props> </property> </bean> |
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> <h3>Error Page</h3> ${exception } ${requestScope.exception } </body> </html> |
- 源码分析
SimpleMappingExceptionResolver L187 L339 |
@Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {
// Expose ModelAndView for chosen error view. String viewName = determineViewName(ex, request); if (viewName != null) { // Apply HTTP status code for error views, if specified. // Only apply it if we're processing a top-level request. Integer statusCode = determineStatusCode(request, viewName); if (statusCode != null) { applyStatusCodeIfPossible(request, response, statusCode); } return getModelAndView(viewName, ex, request); }else { return null; } } |
/** * Return a ModelAndView for the given view name and exception. * <p>The default implementation adds the specified exception attribute. * Can be overridden in subclasses. * @param viewName the name of the error view * @param ex the exception that got thrown during handler execution * @return the ModelAndView instance * @see #setExceptionAttribute */ protected ModelAndView getModelAndView(String viewName, Exception ex) { ModelAndView mv = new ModelAndView(viewName); if (this.exceptionAttribute != null) { if (logger.isDebugEnabled()) { logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'"); } mv.addObject(this.exceptionAttribute, ex); } return mv; } |