spring源码解析-web系列(一):启动
spring源码解析-web系列(二):处理请求的过程
spring源码解析-web系列(三):九大组件之HandlerMapping
spring源码解析-web系列(四):九大组件之HandlerAdapter
spring源码解析-web系列(五):解析请求参数
spring源码解析-web系列(六):九大组件之ViewResolver
spring源码解析-web系列(七):九大组件之HandlerExceptionResolver
转载请标明出处:
https://blog.csdn.net/bingospunky/article/details/98947965
本文出自马彬彬的博客
HandlerExceptionResolver用来解析请求处理过程中产生的异常。
HandlerExceptionResolver的继承关系如下:
图1:
1.AbstractHandlerExceptionResolver:是所有直接解析异常类的父类,定义了通用的解析流程。
2.AbstractHandlerMethodExceptionResolver:和其子类ExceptionHandlerExceptionResolver一起完成对@ExceptionHandler注解方法的处理。
3.DefaultHandlerExceptionResolver:根据异常的类型不同进行处理。
4.ResponseStatusExceptionResolver:解析@ResponseStatus注解类型的异常。
5.SimpleMappingExceptionResolver:通过配置异常类型和View的对应关系解析异常。
本文只介绍一下 AbstractHandlerMethodExceptionResolver 和它的子类,因为@ExceptionHandler是我们常用的异常处理方式,其他的方式很少使用。
代码1 (org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException):
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
// Log exception, both at debug log level and at warn level, if desired.
if (this.logger.isDebugEnabled()) {
this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
}
logException(ex, request);
prepareResponse(ex, response);
return doResolveException(request, response, handler, ex);
}
else {
return null;
}
}
代码1第5行判断是否可以解析这个handler抛出的异常,如果可以解析,则通过代码1第12行调用模板方法doResolveException让子类去解析该异常。判断是否可以解析handler的异常的逻辑也很简单:默认可以即系所有handler的异常;如果用户指定了某些handler对象或handler类型,则以用户指定的为准。
AbstractHandlerMethodExceptionResolver是AbstractHandlerExceptionResolver的子类,主要工作就是对于handler是HandlerMethod时做一些适配。因为在父类的shouldApplyTo中,handler是对象,而对于HandlerMethod,handler是方法,该类通过handler获取它所在的Controller对象给父类的shouldApplyTo使用。该类剩下的一个工作就是抽象了一个 doResolveHandlerMethodException 供子类来解析异常使用。
ExceptionHandlerExceptionResolver解析Exception的过程如下:
代码2 (org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException):
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
} else {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
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);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception});
} catch (Exception var9) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, var9);
}
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
} else {
ModelAndView mav = (new ModelAndView()).addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View)mavContainer.getView());
}
return mav;
}
}
}
代码2第2行通过handlerMethod和exception获取到ServletInvocableHandlerMethod对象,ServletInvocableHandlerMethod对象就是我们 @ExceptionHandler 注解标记的方法。后面的过程和 RequestMappingHandlerAdapter 里的处理流程相似,就像是一个简化版的 RequestMappingHandlerAdapter ,因为他们都是controller里的处理逻辑,区别只是一个处理异常,一个处理用户的请求而已,这里就不展开介绍了,可以参考博客 spring源码解析-web系列(四):九大组件之HandlerAdapter 。
代码2第2行通过handlerMethod和exception获取到ServletInvocableHandlerMethod对象的过程如下:
代码3 (org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.getExceptionHandlerMethod):
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class> handlerType = handlerMethod != null ? handlerMethod.getBeanType() : null;
if (handlerMethod != null) {
ExceptionHandlerMethodResolver resolver = (ExceptionHandlerMethodResolver)this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
Iterator var8 = this.exceptionHandlerAdviceCache.entrySet().iterator();
while(var8.hasNext()) {
Entry entry = (Entry)var8.next();
if (((ControllerAdviceBean)entry.getKey()).isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = (ExceptionHandlerMethodResolver)entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(((ControllerAdviceBean)entry.getKey()).resolveBean(), method);
}
}
}
return null;
}
代码3第2第14行获取当前handlerMethod所在的类里注解了@ExceptionHandler的匹配的方法。代码2第16第27行获取被@ControllerAdvice注解的类里注解了@ExceptionHandler的匹配的方法。
在获取注解了@ExceptionHandler的匹配的方法时,使用的是 ExceptionHandlerMethodResolver 这个类。正如代码3第10行,ExceptionHandlerMethodResolver的resolveMethod方法的处理过程:首先获取类里所有注解了@ExceptionHandler的方法,然后根据@ExceptionHandler方法能处理的异常类型和当前实际的异常类型进行匹配。
HandlerExceptionResolver的作用就是解析异常,具体工作就是:将异常相关信息设置到Model、给response设置响应属性。
mvc:annotation-driven 会将ExceptionHandlerExceptionResolver、DefaultHandlerExceptionResolver、ResponseStatusExceptionResolver设置到DispatcherServlet的handlerExceptionResolvers属性中。
HandlerExceptionResolver只处理请求处理过程中的异常,异常处理本身抛出的异常和渲染视图的异常不归它处理。