HandlerExceptionResolver用于解析请求过程中所产生的异常,继承结构图:
简介:
HandlerExceptionResolver主要实现都继承与抽象类的AbstractHandlerExecutionResolver,它有五个子类,其中AnnotationMethodHandlerExceptionResolver已经被弃用。剩余的
- AbstractHandlerMethodExceptionResolver和其子类ExceptionHandlerExcpetionResolver一起完成@ExceptionHandler注释的方法进行异常解析的功能
- DefaultHandlerExceptionResolver按照不同类型分别对异常进行解析
- ResponseStatusExceptionResolver解析@ResponseStatus注释类型的异常
- SimpleMappingExceptionResolver:通过配置的异常类和view的对应关系解析异常
HandlerExecutionResolverComposite作为一个容器使用,里面封装了别的Resolver。
异常解析的主要过程:给ModelAndView设置相应的内容,设置response的相关属性。当然还可能有一些辅助功能,如记录日志等,在自定义的ExceptionHandler里还可以做更多的事。
1.AbstractHandlerExecutionResolver
它是所有直接解析异常类的父类,里面有通用的解析流程,并使用了模板方法doResolveException(实际处理异常的方法),看源码:
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
//判断当前ExceptionResolver是否可以解析所传入处理器抛出的异常,如果不可以返回null,交给下一个解析
if (this.shouldApplyTo(request, handler)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
}
//设置response
this.prepareResponse(ex, response);
//调用这个实际解析异常,这是模板方法交给子类实现
ModelAndView result = this.doResolveException(request, response, handler, ex);
if (result != null) {
this.logException(ex, request);
}
return result;
} else {
return null;
}
}
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler{
//这里的mappedHandlers用于配置处理器的集合,mappedHandlerClasses用于配置处理器类型的集合,如果设置了这两个属性的一个,那么ExceptionResolver就只能解析所设置的处理器抛出的异常
if (handler != null) {
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true;
}
if (this.mappedHandlerClasses != null) {
Class[] var3 = this.mappedHandlerClasses;
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
Class<?> handlerClass = var3[var5];
if (handlerClass.isInstance(handler)) {
return true;
}
}
}
}
return this.mappedHandlers == null && this.mappedHandlerClasses == null;
}
2.ExceptionHandlerExceptionResolver
这个类继承AbstractHandlerMethodExceptionResolver,后者又继承于AbstractHandlerMethodExceptionResolver重写了shouldApplyTo方法,并且在处理请求的doResolverException方法中将实际处理请求的过程交给了模板方法doResolveHanlerMethodException.
2.1AbstractHandlerMethodExceptionResolver相当于一个适配器,源码如下:
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
public AbstractHandlerMethodExceptionResolver() {
}
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler{
//如果handler为空,交给父类处理
if (handler == null) {
return super.shouldApplyTo(request, (Object)null);
//判断了handler是否属于HandlerMethod,并且将处理器设置为其所在的类,然后交给父类决断
} else if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod)handler;
handler = handlerMethod.getBean();
return super.shouldApplyTo(request, handler);
//如果handler既不为空,并且也不属于handlerMethod直接返回false
} else {
return false;
}
}
@Nullable
protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
return this.doResolveHandlerMethodException(request, response, (HandlerMethod)handler, ex);
}
//留给子类的模板方法
@Nullable
protected abstract ModelAndView doResolveHandlerMethodException(HttpServletRequest var1, HttpServletResponse var2, @Nullable HandlerMethod var3, Exception var4);
}
2.2 ExceptionHandlerExceptionResolver
这个其实也是一个简化版的RequestMappingHandlerAdapter,执行也是使用的ServletInvocableHandlerMethod,首先更具handlerMethod和exception将其创建出来(在处理器类中找出所有注释了@ExceptionHandler的防范,然后根据配置中的异常和需要解析的异常进行匹配),然后设置了argumentResolvers和returnValueHandlers,接着调用其invokeAndHandle方法执行处理,最后封装好结果ModelAndView返回。源码如下:
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
//找到处理异常的方法
ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
} else {
//设置argumentResolvers和returnValueHandlers
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
}
Throwable cause = exception.getCause();
if (cause != null) {
//执行解析异常
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, cause, handlerMethod});
} else {
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, handlerMethod});
}
} catch (Throwable var12) {
if (var12 != exception && this.logger.isWarnEnabled()) {
this.logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, var12);
}
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
} else {
//封装ModelAndView
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View)mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes)model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
}
3.DefaultHandlerExceptionResolver
这个解析过程是根据异常类型不同,使用不同方法进行处理。解析方法代码:
@Nullable
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof HttpRequestMethodNotSupportedException) {
return this.handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException)ex, request, response, handler);
}
if (ex instanceof HttpMediaTypeNotSupportedException) {
return this.handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException)ex, request, response, handler);
}
if (ex instanceof HttpMediaTypeNotAcceptableException) {
return this.handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException)ex, request, response, handler);
}
if (ex instanceof MissingPathVariableException) {
return this.handleMissingPathVariable((MissingPathVariableException)ex, request, response, handler);
}
if (ex instanceof MissingServletRequestParameterException) {
return this.handleMissingServletRequestParameter((MissingServletRequestParameterException)ex, request, response, handler);
}
if (ex instanceof ServletRequestBindingException) {
return this.handleServletRequestBindingException((ServletRequestBindingException)ex, request, response, handler);
}
if (ex instanceof ConversionNotSupportedException) {
return this.handleConversionNotSupported((ConversionNotSupportedException)ex, request, response, handler);
}
if (ex instanceof TypeMismatchException) {
return this.handleTypeMismatch((TypeMismatchException)ex, request, response, handler);
}
if (ex instanceof HttpMessageNotReadableException) {
return this.handleHttpMessageNotReadable((HttpMessageNotReadableException)ex, request, response, handler);
}
if (ex instanceof HttpMessageNotWritableException) {
return this.handleHttpMessageNotWritable((HttpMessageNotWritableException)ex, request, response, handler);
}
if (ex instanceof MethodArgumentNotValidException) {
return this.handleMethodArgumentNotValidException((MethodArgumentNotValidException)ex, request, response, handler);
}
if (ex instanceof MissingServletRequestPartException) {
return this.handleMissingServletRequestPartException((MissingServletRequestPartException)ex, request, response, handler);
}
if (ex instanceof BindException) {
return this.handleBindException((BindException)ex, request, response, handler);
}
if (ex instanceof NoHandlerFoundException) {
return this.handleNoHandlerFoundException((NoHandlerFoundException)ex, request, response, handler);
}
if (ex instanceof AsyncRequestTimeoutException) {
return this.handleAsyncRequestTimeoutException((AsyncRequestTimeoutException)ex, request, response, handler);
}
} catch (Exception var6) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in exception", var6);
}
}
return null;
}
调用的方法一个例子源码:
protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
pageNotFoundLogger.warn(ex.getMessage());
String[] supportedMethods = ex.getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
}
response.sendError(405, ex.getMessage());
return new ModelAndView();
}
从这里可以简单看出来,注释给repsonse设置相应属性和返回一个空ModelAndView,sendError可以返回错误码,通过在页面可以获取到响应信息。sendError和setStatus方法的区别是前者会返回web.xml中定义的错误页面,后者只是设置了status而不会返回响应错误页面,其他处理方法类似。
4.ResponseStatusExceptionResolver
他是用来解析@ResponseStatus的异常(如自定义了注释@ResponseStatus的异常),源码:
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof ResponseStatusException) {
return this.resolveResponseStatusException((ResponseStatusException)ex, request, response, handler);
}
//找到@ResponseStatus注释
ResponseStatus status = (ResponseStatus)AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (status != null) {
//然后调用解析方法
return this.resolveResponseStatus(status, request, response, handler, ex);
}
if (ex.getCause() instanceof Exception) {
ex = (Exception)ex.getCause();
return this.doResolveException(request, response, handler, ex);
}
} catch (Exception var6) {
this.logger.warn("ResponseStatus handling resulted in exception", var6);
}
return null;
}
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
int statusCode = responseStatus.code().value();
String reason = responseStatus.reason();
return this.applyStatusAndReason(statusCode, reason, response);
}
5.SimpleMappingExceptionResolver
这个需要提前配置异常类和view的对应关系然后才能使用,关键方法源码如下:
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
//首先根据异常朝赵显示错误的逻辑视图
String viewName = this.determineViewName(ex, request);
if (viewName != null) {
//检查是否配置了所找到的viewName对应的statusCode
Integer statusCode = this.determineStatusCode(request, viewName);
if (statusCode != null) {
//设置response
this.applyStatusCodeIfPossible(request, response, statusCode);
}
//将异常和解析出的viewName封装成ModelAndView并返回
return this.getModelAndView(viewName, ex, request);
} else {
return null;
}
}
@Nullable
protected String determineViewName(Exception ex, HttpServletRequest request) {
String viewName = null;
//如果异常在设置的excludeExceptions中包含则返回null
if (this.excludedExceptions != null) {
Class[] var4 = this.excludedExceptions;
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
Class<?> excludedEx = var4[var6];
if (excludedEx.equals(ex.getClass())) {
return null;
}
}
}
//调用findMatchingViewName方法实际查找
if (this.exceptionMappings != null) {
viewName = this.findMatchingViewName(this.exceptionMappings, ex);
}
//如果没有找到,并且有默认视图在采用默认视图
if (viewName == null && this.defaultErrorView != null) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" + ex.getClass().getName() + "]");
}
viewName = this.defaultErrorView;
}
return viewName;
}
这个类还有相关的属性解析:
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
@Nullable
private Properties exceptionMappings; //用于配置异常(字符串)和viewName的对应关系,异常类可以是异常的一部分,还可以是异常父类的一部分
@Nullable
private Class<?>[] excludedExceptions;//用于配置不处理的异常
@Nullable
private String defaultErrorView;//用于配置当无法从exceptionMapping中解析出视图使用的默认视图
@Nullable
private Integer defaultStatusCode;//用于配置statusCodes中没有配置相应的viewName时使用的默认statusCode
private Map<String, Integer> statusCodes = new HashMap();//用于配置解除viewName和statusCode对应的关系
@Nullable
private String exceptionAttribute = "exception";//用于配置异常在Model中保存的参数名,默认为exception,如果为null,异常将不保存到Model中
....
}
总结:
mvc:annotation-driven会自动将ExceptionHandlerExceptionREsolver、DefaultHandlerExceptionResolver和ResponseStatusExceptinResolver配置到SpringMVC中,如果SimpleMappingExceptionResolver要使用需要自行配置。
另外这个异常只能处理请求处理过程抛出的异常,异常处理本身抛出的异常和视图解析过程中抛出的异常时不能做到的。