源码基于SpringMVC 5.2.7
HandlerExceptionResolver也是SpringMVC重要组成部分,其重要负责对请求处理过程中的异常处理。HandlerExceptionResolver返回的也是ModelAndView对象。
HandlerExceptionResolver
请求在处理过程中可能会因为各种各样的原因抛出异常,例如参数解析时类型不匹配、参数必传而请求没传。HandlerExceptionResolver处理完异常返回的也是ModelAndView对象。HandlerExceptionResolver是一个接口类,其中只有一个接口方法HandlerExceptionResolver#resolveException。所有异常处理器将实现该接口。
跟HandlerMapping、HandlerAdapter这些组件一样,HandlerExceptionResolver也是在系统初始化的时候装配到DispatcherServlet
- 如果"detectAllHandlerExceptionResolvers"打开,则从IOC容器中获取所有类型为HandlerExceptionResolver的实例;否则进入2
- 从IOC容器中获取name为"handlerExceptionResolver"的实例;
- 如果步骤1、2之后已经有HandlerExceptionResolver则装配过程结束,否则进入4;
- 通过DispatcherServlet默认装配策略中创建HandlerExceptionResolver实例,并装配给DispatcherServlet,装配过程结束。
默认情况“detectAllHandlerAdapters”是打开的,也就是说默认情况是从IOC容器中找到所有HandlerExceptionResolver的Bean。
DispatcherServlet默认装配策略在《抽丝剥茧MVC之RequestMappingHandlerMapping》中有介绍,这里就不赘述。默认策略装配的异常处理器有3个
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolve
DispatcherServlet异常处理
请求处理处理结束(有可能是因为异常终止请求),DispatcherServlet会执行方法processDispatchResult。如果有异常则那么进入到DispatcherServlet#processHandlerException处理异常。
- 依次遍历执行DispatcherServlet中装配好的异常处理器,任何一个异常处理器返回非NULL,则终止遍历
- 如果所有异常处理器都没有返回非空对象,则抛异常表示异常处理本身没成功,否则进入3
- 判断ModelAndView对象是否是Empty(view和model都没有),如果是则返回NULL(告诉后面视图解析逻辑不用执行了,因为异常器已经将响应写会给客户端了)否则进入4
- 如果ModelAndView对象只有model没有view,则设置默认的view。默认的view生成规则去除头尾的"/"和扩展名,参考DefaultRequestToViewNameTranslator#transformPath
public class DispatcherServlet extends FrameworkServlet {
... ...
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
//笔者注:遍历所有的异常处理器,有一个返回非NULL则终止遍历
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
//笔者注:返回的ModelAndView对象非NULL
if (exMv != null) {
//笔者注ModelAndView对象的view和model都是空,则返回NULL,告诉后面视图解析逻辑不用处理,因为异常器已经将响应写会给客户端了。
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
//笔者注:只有model没有view,则获取默认的view
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
else if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
//笔者注:返回的ModelAndView是NULL,则抛异常表示异常处理本身出问题
throw ex;
}
... ...
}
@ExceptionHandler
开发者可以自己实现一些异常处理器。SpringMVC则提供@ExceptionHandler机制,让开发者可以专注异常处理的逻辑。为了处理注解@ExceptionHandler,SpringMVC向系统中注册了异常处理器ExceptionHandlerExceptionResolver。简单说就是@ExceptionHandler给方法打标,表示该方法能处理异常。ExceptionHandlerExceptionResolver根据异常类型匹配到相应的@ExceptionHandler方法,反射执行该方法,返回ModleAndView对象。执行@ExceptionHandler方法与执行请求处理方法一样,也要用到参数解析和返回值处理,只不过两者的参数解析器和返回值处理器不同。
ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver结构
先来看一下ExceptionHandlerExceptionResolver的继承关系
主要成员属性
argumentResolvers:HandlerMethodArgumentResolverComposite类型,ExceptionHandlerExceptionResolver的参数解析器集合
returnValueHandlers:HandlerMethodReturnValueHandlerComposite类型,ExceptionHandlerExceptionResolver的返回处理器集合
messageConverters:HttpMessageConverter类型集合,某些参数解析器及返回处理器需要该组件从request body中读取对象或往response body中写入对象
contentNegotiationManager:ContentNegotiationManager类型,某些参数处理器及返回处理器需要该组件来判断MediaType
exceptionHandlerCache:ExceptionHandlerMethodResolver集合,来自于@Controller,针对当前@Controller的异常处理
exceptionHandlerAdviceCache:ExceptionHandlerMethodResolver集合,来自于@ControllerAdvice的实例,针对所有请求处理器的异常处理
responseBodyAdvice:ResponseBodyAdvice类型集合,某些返回处理器将响应写入response body前后执行的一些逻辑
ExceptionHandlerExceptionResolver初始化
ExceptionHandlerExceptionResolver初始化分两个阶段:构造函数、afterPropertiesSet
构造函数
ExceptionHandlerExceptionResolver构造函数给messageConverters添加了4个元素,分别是:
- org.springframework.http.converter.ByteArrayHttpMessageConverter
- org.springframework.http.converter.StringHttpMessageConverter
- org.springframework.http.converter.xml.SourceHttpMessageConverter
- org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
afterPropertiesSet
afterPropertiesSet是Spring IOC容器创建bean实例的一个阶段,Spring IOC设置完实例的属性后执行afterPropertiesSet。在ExceptionHandlerExceptionResolver#afterPropertiesSet中,做3件事情
其一,初始化exceptionHandlerAdviceCache和responseBodyAdvice
- 从Sring IOC容器中获取所有注解@ControllerAdvice的Bean实例
- 依次遍历,如果实例中有@ExceptionHandler的方法,则添加到exceptionHandlerAdviceCache;如果实例类型是ResponseBodyAdvice及其子类型,则添加到responseBodyAdvice
其二,如果argumentResolvers为NULL,加载ExceptionHandlerExceptionResolver默认的参数解析器
resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor());
其三,如果returnValueHandlers为NULL,加载ExceptionHandlerExceptionResolver默认的返回处理器
// Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor( getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice)); // Annotation-based return value types handlers.add(new ModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor( getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all handlers.add(new ModelAttributeMethodProcessor(true));
ExceptionHandlerExceptionResolver处理异常
1 找到能处理当前异常的@ExceptionHandler方法,注意只找第一个匹配的,返回ServletInvocableHandlerMethod对象
1.1 先从当前请求处理器的异常处理方法(exceptionHandlerCache)中找
1.2 再从全局异常处理方法(exceptionHandlerAdviceCache)中找
2 给ServletInvocableHandlerMethod对象设置参数解析器、返回处理器
3 执行ServletInvocableHandlerMethod#invokeAndHandle处理异常,该过程与RequestMappingHandlerAdapter类似,参考《RequestMappingHandlerAdapter》
3.1 参数解析
3.2 调用异常方法
3.3 返回值处理
4 返回ModelAndView对象
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
... ...
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
//笔者注:找到能处理该异常的@ExceptionHandler方法 (1)当前请求处理器中找 (2)全局异常处理方法中找(@ControllerAdvice)
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
//笔者注:设置参数解析器和返回处理器
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 (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable cause = exception.getCause();
if (cause != null) {
// Expose cause as provided argument as well
//笔者注:执行异常方法,
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// Otherwise, just the given exception as-is
//笔者注:执行异常方法,
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
// Any other than the original exception (or its cause) is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
}
// Continue with default processing of the original exception...
return null;
}
//笔者注: 根据情况返回ModelAndView对象
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
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());
}
//笔者注: FlashMap机制,传参给下一个请求
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
... ...
}
配置
RequestMappingHandlerAdapter配置形式与RequestMappingHandlerMapping一样。RequestMappingHandlerMapping支持的配置方式,RequestMappingHandlerAdapter也都支持。这里不在赘述。参考《RequestMappingHandlerMapping》
目录 目录