SpringMVC 中针对异常问题有一套完整的处理体系,这套体系非常好用,今天松哥就花点时间来和大家聊一聊 SpringMVC 中的异常处理体系,我们把 SpringMVC 中的异常体系从头到尾梳理一下。
1.异常解析器概览
在 SpringMVC 的异常体系中,处于最顶层的大 Boss 是 HandlerExceptionResolver,这是一个接口,里边只有一个方法:
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
resolveException 方法就用来解析请求处理过程中所产生的异常,并最终返回一个 ModelAndView。
我们来看下 HandlerExceptionResolver 的实现类:
直接实现 HandlerExceptionResolver 接口的类有三个:
-
HandlerExceptionResolverComposite:这个一看又是一个组合,在最近的源码分析中我们已经多次见到 xxxComposite 了,这里就不再赘述。
-
DefaultErrorAttributes:这个用来保存异常属性。
-
AbstractHandlerExceptionResolver:这个的子类比较多:
-
SimpleMappingExceptionResolver:通过提前配置好的异常类和 View 之间的对应关系来解析异常。
-
AbstractHandlerMethodExceptionResolver:处理使用
@ExceptionHandler
注解自定义的异常类型。 -
DefaultHandlerExceptionResolver:按照不同类型来处理异常。
-
ResponseStatusExceptionResolver:处理含有
@ResponseStatus
注解的异常。
-
在 SpringMVC 中,大致的异常解析器就是这些,接下来我们来逐个学习这些异常解析器。
2.AbstractHandlerExceptionResolver
AbstractHandlerExceptionResolver 是真正干活的异常解析器的父类,我们就先从他的 resolveException 方法开始看起。
@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
logException(ex, request);
}
return result;
}
else {
return null;
}
}
-
首先调用 shouldApplyTo 方法判断当前解析器是否可以处理传入的处理器所抛出的异常,如果不支持,则直接返回 null,这个异常将交给下一个 HandlerExceptionResolver 去处理。
-
调用 prepareResponse 方法处理 response。
-
调用 doResolveException 方法实际处理异常,这是一个模版方法,具体的实现在子类中。
-
调用 logException 方法记录异常日志信息。
记录异常日志没啥好说的,doResolveException 则是一个空的模版方法,所以这里对我们来说主要就是两个方法:shouldApplyTo 和 prepareResponse,我们分别来看。
shouldApplyTo
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (handler != null) {
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true;
}
if (this.mappedHandlerClasses != null) {
for (Class<?> handlerClass : this.mappedHandlerClasses) {
if (handlerClass.isInstance(handler)) {
return true;
}
}
}
}
return !hasHandlerMappings();
}
这里涉及到了两个对象:mappedHandlers 和 mappedHandlerClasses:
-
mappedHandlers:存储的是处理器对象(Controller 或者 Controller 中的方法)
-
mappedHandlerClasses:存储的是处理器的 Class。
我们在配置异常解析器的时候可以配置这两个对象,进而实现该异常处理器只为某一个处理器服务,但是一般来说没这种需求,所以大家仅做了解即可。
如果开发者一开始配置了 mappedHandlers 或者 mappedHandlerClasses,则用这两个和处理器去比较,否则就直接返回 true,表示支持该异常处理。
prepareResponse
prepareResponse 方法比较简单,主要是处理一下响应头的缓存字段。
protected void prepareResponse(Exception ex, HttpServletResponse response) {
if (this.preventResponseCaching) {
preventCaching(response);
}
}
protected void preventCaching(HttpServletResponse response) {
response.addHeader(HEADER_CACHE_CONTROL, "no-store");
}
这是 AbstractHandlerExceptionResolver 的大致内容,可以看到还是非常 easy 的,接下来我们来看它的实现类。
2.1 AbstractHandlerMethodExceptionResolver
AbstractHandlerMethodExceptionResolver 主要是重写了 shouldApplyTo 方法和 doResolveExceptio