精尽Spring MVC源码分析 - HandlerExceptionResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读
Spring 版本:5.2.4.RELEASE
该系列其他文档请查看:《精尽 Spring MVC 源码分析 – 文章导读》
HandlerExceptionResolver 组件
HandlerExceptionResolver
组件,处理器异常解析器,将处理器( handler
)执行时发生的异常(也就是处理请求,执行方法的过程中)解析(转换)成对应的 ModelAndView 结果
回顾
先来回顾一下在 DispatcherServlet
中处理请求的过程中哪里使用到 HandlerExceptionResolver
组件,可以回到《一个请求的旅行过程》中的 DispatcherServlet
的 processHandlerException
方法中看看,如下:
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
// 移除 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
// <a> 遍历 HandlerExceptionResolver 数组,解析异常,生成 ModelAndView 对象
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
// 遍历 HandlerExceptionResolver 数组
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
// 解析异常,生成 ModelAndView 对象
exMv = resolver.resolveException(request, response, handler, ex);
// 生成成功,结束循环
if (exMv != null) {
break;
}
}
}
// <b> 情况一,生成了 ModelAndView 对象,进行返回
if (exMv != null) {
// ModelAndView 对象为空,则返回 null
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
// 没有视图则设置默认视图
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
// 设置请求中的错误消息属性
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
// <c> 情况二,未生成 ModelAndView 对象,则抛出异常
throw ex;
}
在 Spring MVC 的 DispatcherServlet
处理请求执行方法过程中,不管是否抛出异常都会进行结果处理,如果抛出了异常也需要调用该方法处理异常
可以看到,在 <a>
处会遍历所有的 HandlerExceptionResolver
异常处理器来处理,如果某一个处理器处理成功并返回 ModelAndView 对象,则直接返回
HandlerExceptionResolver 接口
org.springframework.web.servlet.HandlerExceptionResolver
,异常处理器接口,代码如下:
public interface HandlerExceptionResolver {
/**
* 解析异常,转换成对应的 ModelAndView 结果
*/
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
HandlerExceptionResolver 接口体系的结构如下:
初始化过程
在 DispatcherServlet
的 initHandlerExceptionResolvers(ApplicationContext context)
方法,初始化 HandlerExceptionResolver 组件,方法如下:
private void initHandlerExceptionResolvers(ApplicationContext context) {
// 置空 handlerExceptionResolvers 处理
this.handlerExceptionResolvers = null;
// 情况一,自动扫描 HandlerExceptionResolver 类型的 Bean 们
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
// 情况二,获得名字为 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME 的 Bean
else {
try {
HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
/**
* 情况三,如果未获得到,则获得默认配置的 HandlerExceptionResolver 类
* {@link org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver}
* {@link org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver}
* {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}
*/
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
-
如果“开启”探测功能,则扫描已注册的 HandlerExceptionResolver 的 Bean 们,添加到
handlerExceptionResolvers
中,默认开启 -
如果“关闭”探测功能,则获得 Bean 名称为 “handlerExceptionResolver” 对应的 Bean ,将其添加至
handlerExceptionResolvers
-
如果未获得到,则获得默认配置的 HandlerExceptionResolver 类,调用
getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface)
方法,就是从DispatcherServlet.properties
文件中读取 HandlerExceptionResolver 的默认实现类,如下: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.DefaultHandlerExceptionResolver
在 Spring Boot 中,默认配置下会走上述 1
的逻辑,handlerExceptionResolvers
有两个元素:
-
org.springframework.boot.autoconfigure.web.DefaultErrorAttributes
:在 Spring Boot 中,逻辑比较简单,暂时忽略 -
org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
:复合的 HandlerExceptionResolver 实现类
接下来会对 HandlerExceptionResolverComposite
中的这三种异常处理器进行分析
HandlerExceptionResolverComposite
org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
,实现 HandlerExceptionResolver、Ordered 接口,复合的 HandlerExceptionResolver 实现类
构造方法
public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
/**
* 异常解析器数组
*/
@Nullable
private List<HandlerExceptionResolver> resolvers;
/**
* 优先级,默认最低
*/
private int order = Ordered.LOWEST_PRECEDENCE;
}
-
resolvers
:HandlerExceptionResolver 实现类列表 -
order
:优先级,默认最低
从上面的初始化过程中可以看到,Spring Boot 默认配置下 HandlerExceptionResolverComposite 包含三个实现类:
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
resolveException
实现 resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex)
方法,遍历 HandlerExceptionResolver 数组,逐个处理异常 ex
,如果成功,则返回 ModelAndView 对象,方法如下:
@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) {
if (this.resolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}
AbstractHandlerExceptionResolver
org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
,实现 HandlerExceptionResolver、Ordered 接口,HandlerExceptionResolver 抽象类,作为所有 HandlerExceptionResolver 实现类的基类
构造方法
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
private static final String HEADER_CACHE_CONTROL = "Cache-Control";
/**
* 优先级,默认最低
*/
private int order = Ordered.LOWEST_PRECEDENCE;
/**
* 匹配的处理器对象的集合
*/
@Nullable
private Set<?> mappedHandlers;
/**
* 匹配的处理器类型的数组
*/
@Nullable
private Class<?>[] mappedHandlerClasses;
/**
* 防止响应缓存
*/
private boolean preventResponseCaching = false;
}
上面的这些属性在后续方法中会讲到
shouldApplyTo
shouldApplyTo(HttpServletRequest request, Object handler)
方法,判断当前 HandlerExceptionResolver 是否能应用到传入的 handler
处理器,方法如下:
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (handler != null) {
// <1> 如果 mappedHandlers 包含 handler 对象,则返回 true
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true;
}
// <2> 如果 mappedHandlerClasses 包含 handler 的类型,则返回 true
if (this.mappedHandlerClasses != null) {
for (Class<?> handlerClass : this.mappedHandlerClasses) {
if (handlerClass.isInstance(handler)) {
return true;
}
}
}
}
// Else only apply if there are no explicit handler mappings.
// <3> 如果 mappedHandlers 和 mappedHandlerClasses 都为空,说明直接匹配
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
}
- 如果
mappedHandlers
包含该handler
处理器对象,则返回true
- 如果
mappedHandlerClasses
包含该handler
处理器所在类,则返回true
- 如果
mappedHandlers
和mappedHandlerClasses
都为空,说明直接匹配
prepareResponse
prepareResponse(Exception ex, HttpServletResponse response)
方法,阻止响应缓存,方法如下:
protected void prepareResponse(Exception ex, HttpServletResponse response) {
if (this.preventResponseCaching) {
preventCaching(response);
}
}
/**
* Prevents the response from being cached, through setting corresponding
* HTTP {@code Cache-Control: no-store} header.
* @param response current HTTP response
*/
protected void preventCaching(HttpServletResponse response) {
response.addHeader(HEADER_CACHE_CONTROL, "no-store");
}
如果想要阻止响应缓存,需要设置 preventResponseCaching
为 true
resolveException
实现 resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,代码如下:
@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) {
// <1> 判断是否可以应用
if (shouldApplyTo(request, handler)) {
// <1.1> 阻止缓存
prepareResponse(ex, response);
// <1.2> 执行解析异常,返回 ModelAndView 对象
ModelAndView result = doResolveException(request, response, handler, ex);
// <1.3> 如果 ModelAndView 对象非空,则打印日志
if (result != null) {
// Print debug message when warn logger is not enabled.
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
}
// Explicitly configured warn logger in logException method.
logException(ex, request);
}
// <1.4> 返回执行结果
return result;
}
// <2> 不可应用,直接返回 null
else {
return null;
}
}
@Nullable
protected abstract ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
-
调用
shouldApplyTo(HttpServletRequest request, Object handler)
方法,判断是否可以应用,如果可以应用- 调用
prepareResponse(Exception ex, HttpServletResponse response)
方法,阻止缓存 - 调用
doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex)
抽象方法,执行解析异常,返回 ModelAndView 对象 - 如果 ModelAndView 对象非空,则打印日志
- 返回执行结果
- 调用
-
不可应用,直接返回
null
AbstractHandlerMethodExceptionResolver
org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver
,继承 AbstractHandlerExceptionResolver 抽象类,基于 handler
类型为 HandlerMethod 的 HandlerExceptionResolver 抽象类。
可能你会有疑惑,为什么 AbstractHandlerMethodExceptionResolver 只有一个 ExceptionHandlerExceptionResolver 子类,为什么还要做抽象呢?因为 ExceptionHandlerExceptionResolver 是基于 @ExceptionHandler
注解来配置对应的异常处理器,而如果未来我们想自定义其它的方式来配置对应的异常处理器,就可以来继承 AbstractHandlerMethodExceptionResolver 这个抽象类。