1. 概述
本文,我们来分享 HandlerExceptionResolver 组件。在 《精尽 Spring MVC 源码分析 —— 组件一览》 中,我们对它已经做了介绍:
org.springframework.web.servlet.HandlerExceptionResolver
,处理器异常解析器接口,将处理器( handler
)执行时发生的异常,解析( 转换 )成对应的 ModelAndView 结果。代码如下:
// HandlerExceptionResolver.java |
- 也就是说,如果异常被解析成功,则会返回 ModelAndView 对象。
2. 类图
HandlerExceptionResolver 的类图如下:
3. 初始化
仔细一瞅,类还是不少的哈。我们以默认配置的 Spring Boot 场景下为例,来一起看看 DispatcherServlet 的 #initHandlerExceptionResolvers(ApplicationContext context)
方法,初始化 handlerExceptionResolvers
变量。代码如下:
// DispatcherServlet.java |
- 一共有三种情况,初始化
handlerExceptionResolvers
属性。 - 默认情况下,
detectAllHandlerExceptionResolvers
为true
,所以走情况一的逻辑,自动扫描 HandlerExceptionResolver 类型的 Bean 们。在默认配置的 Spring Boot 场景下,handlerExceptionResolvers
的结果是:org.springframework.boot.autoconfigure.web.DefaultErrorAttributes
- HandlerExceptionResolverComposite
所以,我们可以先忽略掉 SpringBoot 中实现的 DefaultErrorAttributes 类,而来到 「4. HandlerExceptionResolverComposite」 中。
艿艿:DefaultErrorAttributes 的代码逻辑非常简单,并且是相对“酱油”的逻辑,胖友自己去瞅瞅即可。
4. HandlerExceptionResolverComposite
org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
,实现 HandlerExceptionResolver、Ordered 接口,复合的 HandlerExceptionResolver 实现类。
4.1 构造方法
// HandlerExceptionResolverComposite.java |
那么,还是让我们来看看,在默认配置的 Spring Boot 场景下,是通过 WebMvcConfigurationSupport 的 #handlerExceptionResolver()
方法,进行初始化。代码如下:
// WebMvcConfigurationSupport.java |
@Bean
注解,注册一个类型为 HandlerExceptionResolver 的 Bean 对象。所以,在 「3. 初始化」 可以被扫描到。<1>
处,创建 HandlerExceptionResolver 数组exceptionResolvers
。<1.1>
处,添加配置的 HandlerExceptionResolver 到exceptionResolvers
中。默认情况下,不会配置。所以感兴趣的胖友,自己去看。<1.2>
处,因为此时exceptionResolvers
为空,所以调用#addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers)
方法,添加默认 HandlerExceptionResolver 数组。代码如下:// WebMvcConfigurationSupport.java
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
// 创建 ExceptionHandlerExceptionResolver 对象
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
exceptionHandlerResolver.setMessageConverters(getMessageConverters());
exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
exceptionHandlerResolver.setResponseBodyAdvice(
Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
if (this.applicationContext != null) {
exceptionHandlerResolver.setApplicationContext(this.applicationContext);
}
exceptionHandlerResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerResolver);
// 创建 ResponseStatusExceptionResolver 对象
ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
// 创建 DefaultHandlerExceptionResolver 对象
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}- 依次创建 ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver 对象,添加到
exceptionResolvers
中。
- 依次创建 ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver 对象,添加到
<1.3>
处,子类定义的 HandlerExceptionResolver 数组,到exceptionResolvers
中。默认情况下,无定义。所以,可以无视先。
<2>
处,创建 HandlerExceptionResolverComposite 数组。
4.2 resolveException
实现 #resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex)
方法,遍历 HandlerExceptionResolver 数组,逐个处理异常 ex
,如果成功,则返回 ModelAndView 对象。代码如下:
// WebMvcConfigurationSupport.java |
5. AbstractHandlerExceptionResolver
org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
,实现 HandlerExceptionResolver、Ordered 接口,HandlerExceptionResolver 抽象类,作为所有 HandlerExceptionResolver 实现类的基类。
5.1 构造方法
// AbstractHandlerExceptionResolver.java |
- 每个属性,我们放在下面的方法,进行详细解析。
5.2 shouldApplyTo
#shouldApplyTo(HttpServletRequest request, Object handler)
方法,判断当前 HandlerExceptionResolver 是否能应用到传入的 handler
处理器。代码如下:
// AbstractHandlerExceptionResolver.java |
- 有
<1>
、<2>
、<3>
种情况,可以满足条件。
5.3 prepareResponse
#prepareResponse(Exception ex, HttpServletResponse response)
方法,阻止响应缓存。代码如下:
// AbstractHandlerExceptionResolver.java |
- 如果想要阻止响应缓存,需要设置
preventResponseCaching
为true
。
5.4 resolveException
实现 #resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,代码如下:
// AbstractHandlerExceptionResolver.java |
- 逻辑非常简单,胖友自己看着注释来瞅瞅即懂。
<1>
处,调用#doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
抽象方法,执行解析异常,返回 ModelAndView 对象。代码如下:// AbstractHandlerExceptionResolver.java
protected abstract ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);- 子类通过实现该抽象方法,实现自己的处理异常逻辑。
<2>
处,调用#logException(Exception ex, HttpServletRequest request)
方法,打印异常日志。代码如下:// AbstractHandlerExceptionResolver.java
protected void logException(Exception ex, HttpServletRequest request) {
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
this.warnLogger.warn(buildLogMessage(ex, request));
}
}
6. AbstractHandlerMethodExceptionResolver
org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver
,继承 AbstractHandlerExceptionResolver 抽象类,基于 handler
类型为 HandlerMethod 的 HandlerExceptionResolver 抽象类。
可能胖友会有疑惑,为什么 AbstractHandlerMethodExceptionResolver 只有一个 ExceptionHandlerExceptionResolver 子类,为什么还要做抽象呢?因为 ExceptionHandlerExceptionResolver 是基于 @ExceptionHandler
注解来配置对应的异常处理器,而如果未来我们想自定义其它的方式来配置对应的异常处理器,就可以来继承 AbstractHandlerMethodExceptionResolver 这个抽象类。😈
艿艿:有没发现 Spring MVC 中,存在大量的逻辑与配置分离的分层实现?嘻嘻
6.1 shouldApplyTo
重写 #shouldApplyTo(HttpServletRequest request, Object handler)
方法,代码如下:
// AbstractHandlerMethodExceptionResolver.java |
- 重点在于情况二,需要在
<x>
处,调用HandlerMethod#getBean()
方法,获得真正的handler
处理器。为什么呢?胖友自己翻翻前面的文章,找找原因。😈
6.2 doResolveException
重写 #doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,代码如下:
// AbstractHandlerMethodExceptionResolver.java |
- 将
handler
转换成 HandlerMethod 类型,并提供新的抽象方法。
7. ExceptionHandlerExceptionResolver
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
,实现 ApplicationContextAware、InitializingBean 接口,继承 AbstractHandlerMethodExceptionResolver 抽象类,基于 @ExceptionHandler
配置 HandlerMethod 的 HandlerExceptionResolver 实现类。
可能有的胖友并没有使用 @ExceptionHandler
注解来实现过异常的处理,可以先看看 《Spring 异常处理 ExceptionHandler 的使用》 。
- 一般情况下,艿艿喜欢使用第三种。
7.1 构造方法
// ExceptionHandlerExceptionResolver.java |
- 和 《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(一)之 HandlerAdapter》 的 「7. RequestMappingHandlerAdapter」 类似,有大量的相同变量,也是最终调用 ServletInvocableHandlerMethod 的方法。😈 可能胖友有点闷逼?继续往下瞅,哈哈哈哈。
7.2 afterPropertiesSet
#afterPropertiesSet()
方法,进一步初始化 ExceptionHandlerExceptionResolver 。代码如下:
// ExceptionHandlerExceptionResolver.java |
<1>
处,调用#initExceptionHandlerAdviceCache()
方法,初始化exceptionHandlerAdviceCache
、responseBodyAdvice
。详细解析,见 「7.2.1 initExceptionHandlerAdviceCache」 。<2>
处,初始化argumentResolvers
属性。其中,#getDefaultArgumentResolvers()
方法,获得默认的 HandlerMethodArgumentResolver 数组。详细解析,见 「7.2.2 getDefaultArgumentResolvers」 。<3>
处,初始化returnValueHandlers
属性。其中,#getDefaultReturnValueHandlers()
方法,获得默认的 HandlerMethodReturnValueHandler 数组。详细解析,见 「7.2.3 getDefaultReturnValueHandlers」 。
7.2.1 initExceptionHandlerAdviceCache
#initExceptionHandlerAdviceCache()
方法,初始化 exceptionHandlerAdviceCache
、responseBodyAdvice
。代码如下:
// ExceptionHandlerExceptionResolver.java |
<1>
处,调用ControllerAdviceBean#findAnnotatedBeans(ApplicationContext context)
方法,扫描@ControllerAdvice
注解的 Bean 们,并将进行排序。可能有胖友不熟悉这个注解,可以看看 《Spring 3.2 新注解 @ControllerAdvice》 。<2>
处,遍历 ControllerAdviceBean 数组。<2.1>
+<2.2>
处,扫描该 ControllerAdviceBean 对应的类型,如果有@ExceptionHandler
注解,则添加到exceptionHandlerAdviceCache
中。关于 ExceptionHandlerMethodResolver 类,胖友可以先跳到 「7.3 ExceptionHandlerMethodResolver」 小节,看完后回来。<2.3>
处,如果该beanType
类型是 ResponseBodyAdvice 子类,则添加到responseBodyAdvice
中。
7.2.2 getDefaultArgumentResolvers
#getDefaultArgumentResolvers()
方法,获得默认的 HandlerMethodArgumentResolver 数组。见 传送门 。
7.2.3 getDefaultReturnValueHandlers
#getDefaultReturnValueHandlers()
方法,获得默认的 HandlerMethodReturnValueHandler 数组。见 传送门 。
7.3 ExceptionHandlerMethodResolver
艿艿:关于 ExceptionHandlerMethodResolver 类,因为只有 ExceptionHandlerExceptionResolver 类在用,所以放在此处。😈 不过 ExceptionHandlerExceptionResolver 的类名,看起来好容易混淆。。。
org.springframework.web.method.annotation.ExceptionHandlerMethodResolver
,注解了 @ExceptionHandler
的方法的解析器。
7.3.1 构造方法
// ExceptionHandlerMethodResolver.java |
mappedMethods
和exceptionLookupCache
差别在于,后者是经过查找,比较优先级后所产生的。<1>
处,遍历@ExceptionHandler
注解的方法。<2>
处,调用#detectExceptionMappings(Method method)
方法,获得方法的异常数组。代码如下:// ExceptionHandlerMethodResolver.java
private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<>();
// 首先,从方法上的 @ExceptionHandler 注解中,获得所处理的异常,添加到 result 中
detectAnnotationExceptionMappings(method, result);
// 其次,如果获取不到,从方法参数中,获得所处理的异常,添加到 result 中
if (result.isEmpty()) {
for (Class<?> paramType : method.getParameterTypes()) {
if (Throwable.class.isAssignableFrom(paramType)) {
result.add((Class<? extends Throwable>) paramType);
}
}
}
// 如果获取不到,则抛出 IllegalStateException 异常
if (result.isEmpty()) {
throw new IllegalStateException("No exception types mapped to " + method);
}
return result;
}
private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
Assert.state(ann != null, "No ExceptionHandler annotation");
result.addAll(Arrays.asList(ann.value()));
}<3>
处,调用#addExceptionMapping(Class<? extends Throwable> exceptionType, Method method)
方法,添加到mappedMethods
中。代码如下:// ExceptionHandlerMethodResolver.java
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
// 添加到 mappedMethods 中
Method oldMethod = this.mappedMethods.put(exceptionType, method);
// 如果已存在,说明冲突,所以抛出 IllegalStateException 异常
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}
7.3.2 hasExceptionMappings
#hasExceptionMappings()
方法,判断 mappedMethods
非空。代码如下:
// ExceptionHandlerMethodResolver.java |
7.3.3 resolveMethod
#resolveMethod(Exception exception)
方法,解析异常对应的方法。代码如下:
// ExceptionHandlerMethodResolver.java |
按照
exception
和exception.cause
的先后,调用#resolveMethodByExceptionType(Class<? extends Throwable> exceptionType)
方法,获得异常对应的方法。代码如下:// ExceptionHandlerMethodResolver.java
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
// 首先,先从 exceptionLookupCache 缓存中获得
Method method = this.exceptionLookupCache.get(exceptionType);
// 其次,获取不到,则从 mappedMethods 中获得,并添加到 exceptionLookupCache 中
if (method == null) {
method = getMappedMethod(exceptionType);
this.exceptionLookupCache.put(exceptionType, method);
}
return method;
}- 代码比较简单,胖友自己瞅瞅。
调用
#getMappedMethod(Class<? extends Throwable> exceptionType)
方法,获得异常对应的方法。代码如下:// ExceptionHandlerMethodResolver.java
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
// 遍历 mappedMethods 数组,匹配异常,添加到 matches 中
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 将匹配的结果,排序,选择第一个
if (!matches.isEmpty()) {
matches.sort(new ExceptionDepthComparator(exceptionType));
return this.mappedMethods.get(matches.get(0));
} else {
return null;
}
}- 代码还是比较简单,胖友自己瞅瞅。
- 关于
org.springframework.core.ExceptionDepthComparator
比较器,胖友自己点击 传送门 查看。大体的逻辑是,比较它们和目标类的继承层级,越小越匹配。
7.4 getExceptionHandlerMethod
#getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception)
方法,获得异常对应的 ServletInvocableHandlerMethod 对象。代码如下:
// ExceptionHandlerMethodResolver.java |
- 虽然代码比较多,但是总体分成
<1>
、<2>
两大种情况。 - ========== 第一种
@Controller
级 ========== <1>
处,首先,如果handlerMethod
非空,则先获得 Controller 对应的@ExceptionHandler
处理器对应的方法。- 剩余的部分,胖友看代码注释
- ========== 第二种
@ControllerAdvice
级 ========== <2>
处,其次,使用 ControllerAdvice 对应的@ExceptionHandler
处理器对应的方法。- 剩余的部分,胖友看代码注释
- ========= 分割线 =========
- 当然,也有可能获取不到的情况,则会返回
null
。
7.5 doResolveHandlerMethodException
实现 #doResolveHandlerMethodException(ttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception)
方法,代码如下:
// ExceptionHandlerMethodResolver.java |
<1>
处,调用#getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception)
方法,获得异常对应的 ServletInvocableHandlerMethod 对象。详细解析,见 「7.4 getExceptionHandlerMethod」 。<1.1>
处,设置 ServletInvocableHandlerMethod 对象的相关属性。
<1.2>
处,创建 ServletWebRequest 对象。<1.3>
处,创建 ModelAndViewContainer 对象。- 【重要】
<2>
处,执行 ServletInvocableHandlerMethod 的调用。- 在 《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》 中,已经详细解析。如果不太记得的胖友,回去复习下。
- 【也很重要】比较特别的是,此处传入了
Object... providedArgs
参数为exception
和handlerMethod
变量,这也是为什么@ExceptionHanlder
注解的方法,可以设置为这两个参数。😈 <2.1>
处,发生异常,则直接返回。
<3.1>
处,如果mavContainer
已处理,则返回“空”的 ModelAndView 对象。😈 这样,就不会被后续的 ViewResolver 所处理。为什么呢?胖友自己回看下 DispatcherServlet 的#processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,就很容易明白。没有明白的话,仔细思考下,或者来星球讨论一波。<3.2>
处,如果mavContainer
未处,则基于mavContainer
生成 ModelAndView 对象。<3.2.1>
处,创建 ModelAndView 对象,并设置相关属性。<3.2.2>
处, TODO 1004 flashMapManager
8. ResponseStatusExceptionResolver
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
,实现 MessageSourceAware 接口,继承 AbstractHandlerExceptionResolver 抽象类,基于 @ResponseStatus
提供错误响应的 HandlerExceptionResolver 实现类。
8.1 构造方法
// ResponseStatusExceptionResolver.java |
8.2 applyStatusAndReason
#applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
方法,设置错误响应。代码如下:
// ResponseStatusExceptionResolver.java |
- 注意,此处返回的也是“空”的 ModelAndView 对象。
8.3 doResolveException
实现 #doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,代码如下:
// ResponseStatusExceptionResolver.java |
- 分成三种情况。
<1>
处,情况一,如果异常是 ResponseStatusException 类型,调用#resolveResponseStatus(ResponseStatusException ex, HttpServletRequest request, HttpServletResponse response, Object handler)
方法,进行解析并设置到响应。代码如下:// ResponseStatusExceptionResolver.java
protected ModelAndView resolveResponseStatusException(ResponseStatusException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception {
int statusCode = ex.getStatus().value();
String reason = ex.getReason();
return applyStatusAndReason(statusCode, reason, response);
}<2>
处,情况二,如果异常有@ResponseStatus
注解,调用#resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,进行解析并设置到响应。代码如下:// ResponseStatusExceptionResolver.java
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 applyStatusAndReason(statusCode, reason, response);
}<3>
处,情况三,使用异常的cause
在走一次情况一、情况二的逻辑。
9. DefaultHandlerExceptionResolver
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
,继承 AbstractHandlerExceptionResolver 抽象类,默认 HandlerExceptionResolver 实现类,针对各种异常,设置错误响应。
其中,实现 #doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,代码如下:
// DefaultHandlerExceptionResolver.java |
比较简单,就不啰嗦解析。感兴趣的胖友,自己去瞅瞅这个类即可。
10. SimpleMappingExceptionResolver
艿艿:这个类是选读的,不敢兴趣的胖友,可以绕过。
org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
,继承 AbstractHandlerExceptionResolver 抽象类,是 Spring MVC 提供的一个简易匹配的异常处理方式。
可通过 XML 中进行配置,示例如下:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> |
10.1 构造方法
// SimpleMappingExceptionResolver.java |
- 属性比较多,随着下面的方法,一起瞅瞅。
10.2 doResolveException
实现 #doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
方法,代码如下:
// SimpleMappingExceptionResolver.java |
<1>
处,调用#determineViewName(Exception ex, HttpServletRequest request)
方法,获得异常对应的视图。代码如下:// SimpleMappingExceptionResolver.java
protected String determineViewName(Exception ex, HttpServletRequest request) {
String viewName = null;
// 如果是排除的异常,返回 null
if (this.excludedExceptions != null) {
for (Class<?> excludedEx : this.excludedExceptions) {
if (excludedEx.equals(ex.getClass())) {
return null;
}
}
}
// Check for specific exception mappings.
// 获得异常对应的视图
if (this.exceptionMappings != null) {
viewName = findMatchingViewName(this.exceptionMappings, ex);
}
// Return default error view else, if defined.
// 如果获得不到视图,并且有默认视图,则使用默认视图
if (viewName == null && this.defaultErrorView != null) {
if (logger.isDebugEnabled()) {
logger.debug("Resolving to default view '" + this.defaultErrorView + "'");
}
viewName = this.defaultErrorView;
}
return viewName;
}其中,调用
#findMatchingViewName(Properties exceptionMappings, Exception ex)
方法,获得异常对应的视图。代码如下:// SimpleMappingExceptionResolver.java
protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
String viewName = null;
String dominantMapping = null;
int deepest = Integer.MAX_VALUE;
// 遍历 exceptionMappings 数组,寻找最匹配的视图名
for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
String exceptionMapping = (String) names.nextElement();
// 获得层级
int depth = getDepth(exceptionMapping, ex);
// 如果层级更低,则使用它
if (depth >= 0 && (depth < deepest || (depth == deepest &&
dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) { // 层级相同,类全名更长,则选择它
deepest = depth;
dominantMapping = exceptionMapping;
viewName = exceptionMappings.getProperty(exceptionMapping);
}
}
// 返回 viewName
if (viewName != null && logger.isDebugEnabled()) {
logger.debug("Resolving to view '" + viewName + "' based on mapping [" + dominantMapping + "]");
}
return viewName;
}
protected int getDepth(String exceptionMapping, Exception ex) {
return getDepth(exceptionMapping, ex.getClass(), 0);
}
private int getDepth(String exceptionMapping, Class<?> exceptionClass, int depth) {
// 匹配上
if (exceptionClass.getName().contains(exceptionMapping)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
// 未匹配上
if (exceptionClass == Throwable.class) {
return -1;
}
// 递归父类,继续匹配
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
}- 简单,胖友自己瞅瞅即可。
<2>
处,调用#determineStatusCode(HttpServletRequest request, String viewName)
方法,获得视图对应的状态码。代码如下:// SimpleMappingExceptionResolver.java
protected Integer determineStatusCode(HttpServletRequest request, String viewName) {
// 从 statusCodes 中,获得视图名对应的状态码
if (this.statusCodes.containsKey(viewName)) {
return this.statusCodes.get(viewName);
}
// 获得不到,使用默认状态码
return this.defaultStatusCode;
}<3>
处,调用#applyStatusCodeIfPossible(HttpServletRequest request, HttpServletResponse response, int statusCode)
方法,设置状态码到响应。代码如下:// SimpleMappingExceptionResolver.java
protected void applyStatusCodeIfPossible(HttpServletRequest request, HttpServletResponse response, int statusCode) {
if (!WebUtils.isIncludeRequest(request)) {
if (logger.isDebugEnabled()) {
logger.debug("Applying HTTP status " + statusCode);
}
response.setStatus(statusCode);
request.setAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE, statusCode);
}
}<4>
处,调用#getModelAndView(String viewName, Exception ex)
方法,创建 ModelAndView 对象。代码如下:// SimpleMappingExceptionResolver.java
protected ModelAndView getModelAndView(String viewName, Exception ex) {
ModelAndView mv = new ModelAndView(viewName);
// 添加 exceptionAttribute
if (this.exceptionAttribute != null) {
mv.addObject(this.exceptionAttribute, ex);
}
return mv;
}
666. 彩蛋
虽然很长,但是实际上,灰常简单。嘿嘿。
参考和推荐如下文章: