# 精尽 Spring MVC 源码解析 —— HandlerExceptionResolver 组件 # 1. 概述 本文,我们来分享 HandlerExceptionResolver 组件。在 [《精尽 Spring MVC 源码分析 —— 组件一览》](http://svip.iocoder.cn/Spring-MVC/Components-intro) 中,我们对它已经做了介绍: `org.springframework.web.servlet.HandlerExceptionResolver` ,处理器异常解析器接口,将处理器( `handler` )执行时发生的异常,解析( 转换 )成对应的 ModelAndView 结果。代码如下: ``` // HandlerExceptionResolver.java public interface HandlerExceptionResolver { /** * 解析异常,转换成对应的 ModelAndView 结果 */ @Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); } ``` - 也就是说,如果异常被**解析成功**,则会返回 ModelAndView 对象。 # 2. 类图 HandlerExceptionResolver 的类图如下: [![类图](19-Spring MVC 源码解析-HandlerExceptionResolver 组件.assets/01.png)](http://static.iocoder.cn/images/Spring/2022-06-01/01.png)类图 # 3. 初始化 仔细一瞅,类还是不少的哈。我们以**默认配置的 Spring Boot 场景**下为例,来一起看看 DispatcherServlet 的 `#initHandlerExceptionResolvers(ApplicationContext context)` 方法,初始化 `handlerExceptionResolvers` 变量。代码如下: ``` // DispatcherServlet.java /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean?. */ private boolean detectAllHandlerExceptionResolvers = true; @Nullable private List handlerExceptionResolvers; private void initHandlerExceptionResolvers(ApplicationContext context) { // 置空 handlerExceptionResolvers 处理 this.handlerExceptionResolvers = null; // 情况一,自动扫描 HandlerExceptionResolver 类型的 Bean 们 if (this.detectAllHandlerExceptionResolvers) { // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts. Map 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 类 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"); } } } ``` - 一共有三种情况,初始化 `handlerExceptionResolvers` 属性。 - 默认情况下, ``` detectAllHandlerExceptionResolvers ``` 为 ``` true ``` ,所以走 情况一 的逻辑,自动扫描 HandlerExceptionResolver 类型的 Bean 们。在 默认配置的 Spring Boot 场景 下, ``` handlerExceptionResolvers ``` 的结果是: - `org.springframework.boot.autoconfigure.web.DefaultErrorAttributes` - HandlerExceptionResolverComposite 所以,我们可以先忽略掉 SpringBoot 中实现的 DefaultErrorAttributes 类,而来到 [「4. HandlerExceptionResolverComposite」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 中。 > 艿艿:DefaultErrorAttributes 的代码逻辑非常简单,并且是相对“酱油”的逻辑,胖友自己去瞅瞅即可。 # 4. HandlerExceptionResolverComposite `org.springframework.web.servlet.handler.HandlerExceptionResolverComposite` ,实现 HandlerExceptionResolver、Ordered 接口,复合的 HandlerExceptionResolver 实现类。 ## 4.1 构造方法 ``` // HandlerExceptionResolverComposite.java /** * resolvers 数组 */ @Nullable private List resolvers; /** * 优先级,最低 */ private int order = Ordered.LOWEST_PRECEDENCE; ``` 那么,还是让我们来看看,在**默认配置的 Spring Boot 场景**下,是通过 WebMvcConfigurationSupport 的 `#handlerExceptionResolver()` 方法,进行初始化。代码如下: ``` // WebMvcConfigurationSupport.java @Bean public HandlerExceptionResolver handlerExceptionResolver() { // <1> 创建 HandlerExceptionResolver 数组 List exceptionResolvers = new ArrayList<>(); // <1.1> 添加配置的 HandlerExceptionResolver 到 exceptionResolvers 中 configureHandlerExceptionResolvers(exceptionResolvers); // <1.2> 如果 exceptionResolvers 为空,添加默认 HandlerExceptionResolver 数组 if (exceptionResolvers.isEmpty()) { addDefaultHandlerExceptionResolvers(exceptionResolvers); } // <1.3> 子类定义的 HandlerExceptionResolver 数组,到 exceptionResolvers 中 extendHandlerExceptionResolvers(exceptionResolvers); // <2> 创建 HandlerExceptionResolverComposite 数组 HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); composite.setOrder(0); composite.setExceptionResolvers(exceptionResolvers); return composite; } ``` - `@Bean` 注解,注册一个类型为 HandlerExceptionResolver 的 Bean 对象。所以,在 [「3. 初始化」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 可以被扫描到。 - `<1>` 处,创建 HandlerExceptionResolver 数组 `exceptionResolvers` 。 - `<1.1>` 处,添加配置的 HandlerExceptionResolver 到 `exceptionResolvers` 中。默认情况下,不会配置。所以感兴趣的胖友,自己去看。 - `<1.2>` 处,因为此时 `exceptionResolvers` 为空,所以调用 `#addDefaultHandlerExceptionResolvers(List exceptionResolvers)` 方法,添加**默认** HandlerExceptionResolver 数组。代码如下: ``` // WebMvcConfigurationSupport.java protected final void addDefaultHandlerExceptionResolvers(List 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` 中。 - `<1.3>` 处,子类定义的 HandlerExceptionResolver 数组,到 `exceptionResolvers` 中。默认情况下,无定义。所以,可以无视先。 - `<2>` 处,创建 HandlerExceptionResolverComposite 数组。 ## 4.2 resolveException 实现 `#resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex)` 方法,遍历 HandlerExceptionResolver 数组,逐个处理异常 `ex` ,如果成功,则返回 ModelAndView 对象。代码如下: ``` // WebMvcConfigurationSupport.java @Override @Nullable public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler,Exception ex) { if (this.resolvers != null) { // 遍历 HandlerExceptionResolver 数组,逐个处理异常 ex ,如果成功,则返回 ModelAndView 对象 for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) { ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex); if (mav != null) { return mav; } } } return null; } ``` # 5. AbstractHandlerExceptionResolver `org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver`,实现 HandlerExceptionResolver、Ordered 接口,HandlerExceptionResolver 抽象类,作为所有 HandlerExceptionResolver 实现类的**基类**。 ## 5.1 构造方法 ``` // AbstractHandlerExceptionResolver.java /** * 顺序,优先级最低 */ private int order = Ordered.LOWEST_PRECEDENCE; /** * 匹配的处理器对象的集合 */ @Nullable private Set mappedHandlers; /** * 匹配的处理器类型的数组 */ @Nullable private Class[] mappedHandlerClasses; @Nullable private Log warnLogger; /** * 防止响应缓存 */ private boolean preventResponseCaching = false; ``` - 每个属性,我们放在下面的方法,进行详细解析。 ## 5.2 shouldApplyTo `#shouldApplyTo(HttpServletRequest request, Object handler)` 方法,判断当前 HandlerExceptionResolver 是否能应用到传入的 `handler` 处理器。代码如下: ``` // AbstractHandlerExceptionResolver.java 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); } ``` - 有 `<1>`、`<2>`、`<3>` 种情况,可以满足条件。 ## 5.3 prepareResponse `#prepareResponse(Exception ex, HttpServletResponse response)` 方法,阻止响应缓存。代码如下: ``` // AbstractHandlerExceptionResolver.java private static final String HEADER_CACHE_CONTROL = "Cache-Control"; protected void prepareResponse(Exception ex, HttpServletResponse response) { if (this.preventResponseCaching) { preventCaching(response); } } protected void preventCaching(HttpServletResponse response) { response.addHeader(HEADER_CACHE_CONTROL, "no-store"); } ``` - 如果想要阻止响应缓存,需要设置 `preventResponseCaching` 为 `true` 。 ## 5.4 resolveException 实现 `#resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法,代码如下: ``` // AbstractHandlerExceptionResolver.java @Override @Nullable public ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { // 判断是否可以应用 if (shouldApplyTo(request, handler)) { // 阻止缓存 prepareResponse(ex, response); // 执行解析异常,返回 ModelAndView 对象 ModelAndView result = doResolveException(request, response, handler, ex); // 如果 ModelAndView 对象非空,则进行返回 if (result != null) { // Print warn message when warn logger is not enabled... if (logger.isWarnEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) { logger.warn("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result)); } // 打印异常日志 // warnLogger with full stack trace (requires explicit config) logException(ex, request); } // 返回 ModelAndView 对象 return result; // 不可应用,直接返回 null } else { return null; } } ``` - 逻辑非常简单,胖友自己看着注释来瞅瞅即懂。 - `<1>` 处,调用 `#doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` **抽象**方法,执行解析异常,返回 ModelAndView 对象。代码如下: ``` // AbstractHandlerExceptionResolver.java @Nullable 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 @Override protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) { // 情况一,如果 handler 为空,则直接调用父方法 if (handler == null) { return super.shouldApplyTo(request, null); // 情况二,处理 handler 为 HandlerMethod 类型的情况 } else if (handler instanceof HandlerMethod) { // 获得真正的 handler HandlerMethod handlerMethod = (HandlerMethod) handler; handler = handlerMethod.getBean(); // 调用父方法 return super.shouldApplyTo(request, handler); // 情况三,直接返回 false } else { return false; } } ``` - 重点在于情况二,需要在 `` 处,调用 `HandlerMethod#getBean()` 方法,获得真正的 `handler` 处理器。为什么呢?胖友自己翻翻前面的文章,找找原因。😈 ## 6.2 doResolveException 重写 `#doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法,代码如下: ``` // AbstractHandlerMethodExceptionResolver.java @Override @Nullable protected final ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex); } @Nullable protected abstract ModelAndView doResolveHandlerMethodException( HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception ex); ``` - 将 `handler` 转换成 HandlerMethod 类型,并提供新的抽象方法。 # 7. ExceptionHandlerExceptionResolver `org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver` ,实现 ApplicationContextAware、InitializingBean 接口,继承 AbstractHandlerMethodExceptionResolver 抽象类,基于 `@ExceptionHandler` 配置 HandlerMethod 的 HandlerExceptionResolver 实现类。 可能有的胖友并没有使用 `@ExceptionHandler` 注解来实现过异常的处理,可以先看看 [《Spring 异常处理 ExceptionHandler 的使用》](https://www.jianshu.com/p/12e1a752974d) 。 - 一般情况下,艿艿喜欢使用**第三种**。 ## 7.1 构造方法 ``` // ExceptionHandlerExceptionResolver.java @Nullable private List customArgumentResolvers; @Nullable private HandlerMethodArgumentResolverComposite argumentResolvers; @Nullable private List customReturnValueHandlers; @Nullable private HandlerMethodReturnValueHandlerComposite returnValueHandlers; private List> messageConverters; private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); private final List responseBodyAdvice = new ArrayList<>(); @Nullable private ApplicationContext applicationContext; private final Map, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64); private final Map exceptionHandlerAdviceCache = new LinkedHashMap<>(); public ExceptionHandlerExceptionResolver() { StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 // 初始化 messageConverters this.messageConverters = new ArrayList<>(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); } ``` - 和 [《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(一)之 HandlerAdapter》](http://svip.iocoder.cn/Spring-MVC/HandlerAdapter-1-HandlerAdapter) 的 [「7. RequestMappingHandlerAdapter」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 类似,有大量的相同变量,也是最终调用 ServletInvocableHandlerMethod 的方法。😈 可能胖友有点闷逼?继续往下瞅,哈哈哈哈。 ## 7.2 afterPropertiesSet `#afterPropertiesSet()` 方法,进一步初始化 ExceptionHandlerExceptionResolver 。代码如下: ``` // ExceptionHandlerExceptionResolver.java @Override public void afterPropertiesSet() { // Do this first, it may add ResponseBodyAdvice beans // 初始化 exceptionHandlerAdviceCache、responseBodyAdvice initExceptionHandlerAdviceCache(); // 初始化 argumentResolvers 参数 if (this.argumentResolvers == null) { List resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 初始化 returnValueHandlers 参数 if (this.returnValueHandlers == null) { List handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } } ``` - `<1>` 处,调用 `#initExceptionHandlerAdviceCache()` 方法,初始化 `exceptionHandlerAdviceCache`、`responseBodyAdvice` 。详细解析,见 [「7.2.1 initExceptionHandlerAdviceCache」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 。 - `<2>` 处,初始化 `argumentResolvers` 属性。其中,`#getDefaultArgumentResolvers()` 方法,获得默认的 HandlerMethodArgumentResolver 数组。详细解析,见 [「7.2.2 getDefaultArgumentResolvers」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 。 - `<3>` 处,初始化 `returnValueHandlers` 属性。其中,`#getDefaultReturnValueHandlers()` 方法,获得默认的 HandlerMethodReturnValueHandler 数组。详细解析,见 [「7.2.3 getDefaultReturnValueHandlers」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 。 ### 7.2.1 initExceptionHandlerAdviceCache `#initExceptionHandlerAdviceCache()` 方法,初始化 `exceptionHandlerAdviceCache`、`responseBodyAdvice` 。代码如下: ``` // ExceptionHandlerExceptionResolver.java private void initExceptionHandlerAdviceCache() { if (getApplicationContext() == null) { return; } // <1> 扫描 @ControllerAdvice 注解的 Bean 们,并将进行排序 List adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); AnnotationAwareOrderComparator.sort(adviceBeans); // <2> 遍历 ControllerAdviceBean 数组 for (ControllerAdviceBean adviceBean : adviceBeans) { Class beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } // <2.1> 扫描该 ControllerAdviceBean 对应的类型 ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType); // <2.2> 有 @ExceptionHandler 注解,则添加到 exceptionHandlerAdviceCache 中 if (resolver.hasExceptionMappings()) { this.exceptionHandlerAdviceCache.put(adviceBean, resolver); } // <2.3> 如果该 beanType 类型是 ResponseBodyAdvice 子类,则添加到 responseBodyAdvice 中 if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) { this.responseBodyAdvice.add(adviceBean); } } // 打印日志 if (logger.isDebugEnabled()) { int handlerSize = this.exceptionHandlerAdviceCache.size(); int adviceSize = this.responseBodyAdvice.size(); if (handlerSize == 0 && adviceSize == 0) { logger.debug("ControllerAdvice beans: none"); } else { logger.debug("ControllerAdvice beans: " + handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice"); } } } ``` - `<1>` 处,调用 `ControllerAdviceBean#findAnnotatedBeans(ApplicationContext context)` 方法,扫描 `@ControllerAdvice` 注解的 Bean 们,并将进行排序。可能有胖友不熟悉这个注解,可以看看 [《Spring 3.2 新注解 @ControllerAdvice》](http://jinnianshilongnian.iteye.com/blog/1866350) 。 - ``` <2> ``` 处,遍历 ControllerAdviceBean 数组。 - `<2.1>` + `<2.2>` 处,扫描该 ControllerAdviceBean 对应的类型,如果有 `@ExceptionHandler` 注解,则添加到 `exceptionHandlerAdviceCache` 中。关于 ExceptionHandlerMethodResolver 类,胖友可以先跳到 [「7.3 ExceptionHandlerMethodResolver」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 小节,看完后回来。 - `<2.3>` 处,如果该 `beanType` 类型是 ResponseBodyAdvice 子类,则添加到 `responseBodyAdvice` 中。 ### 7.2.2 getDefaultArgumentResolvers `#getDefaultArgumentResolvers()` 方法,获得默认的 HandlerMethodArgumentResolver 数组。见 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java#L318-L341) 。 ### 7.2.3 getDefaultReturnValueHandlers `#getDefaultReturnValueHandlers()` 方法,获得默认的 HandlerMethodReturnValueHandler 数组。见 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java#L343-L375) 。 ## 7.3 ExceptionHandlerMethodResolver > 艿艿:关于 ExceptionHandlerMethodResolver 类,因为只有 ExceptionHandlerExceptionResolver 类在用,所以放在此处。😈 不过 ExceptionHandlerExceptionResolver 的类名,看起来好容易混淆。。。 `org.springframework.web.method.annotation.ExceptionHandlerMethodResolver`,注解了 `@ExceptionHandler` 的方法的解析器。 ### 7.3.1 构造方法 ``` // ExceptionHandlerMethodResolver.java /** * A filter for selecting {@code @ExceptionHandler} methods. * * MethodFilter 对象,用于过滤带有 @ExceptionHandler 注解的方法 */ public static final MethodFilter EXCEPTION_HANDLER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class); /** * 已经映射的方法 * * 在 {@link #ExceptionHandlerMethodResolver(Class)} 构造方法中初始化 */ private final Map, Method> mappedMethods = new HashMap<>(16); /** * 已经匹配的方法 * * 在 {@link #resolveMethod(Exception)} 方法中初始化 */ private final Map, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16); public ExceptionHandlerMethodResolver(Class handlerType) { // <1> 遍历 @ExceptionHandler 注解的方法 for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) { // <2> 遍历处理的异常集合 for (Class exceptionType : detectExceptionMappings(method)) { // <3> 添加到 mappedMethods 中 addExceptionMapping(exceptionType, method); } } } ``` - `mappedMethods` 和 `exceptionLookupCache` 差别在于,后者是经过查找,比较优先级后所产生的。 - `<1>` 处,遍历 `@ExceptionHandler` 注解的方法。 - `<2>` 处,调用 `#detectExceptionMappings(Method method)` 方法,获得方法的异常数组。代码如下: ``` // ExceptionHandlerMethodResolver.java private List> detectExceptionMappings(Method method) { List> 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) paramType); } } } // 如果获取不到,则抛出 IllegalStateException 异常 if (result.isEmpty()) { throw new IllegalStateException("No exception types mapped to " + method); } return result; } private void detectAnnotationExceptionMappings(Method method, List> result) { ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class); Assert.state(ann != null, "No ExceptionHandler annotation"); result.addAll(Arrays.asList(ann.value())); } ``` - `<3>` 处,调用 `#addExceptionMapping(Class exceptionType, Method method)` 方法,添加到 `mappedMethods` 中。代码如下: ``` // ExceptionHandlerMethodResolver.java private void addExceptionMapping(Class 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 public boolean hasExceptionMappings() { return !this.mappedMethods.isEmpty(); } ``` ### 7.3.3 resolveMethod `#resolveMethod(Exception exception)` 方法,解析异常对应的方法。代码如下: ``` // ExceptionHandlerMethodResolver.java @Nullable public Method resolveMethod(Exception exception) { return resolveMethodByThrowable(exception); } @Nullable public Method resolveMethodByThrowable(Throwable exception) { // 首先,获得异常对应的方法 Method method = resolveMethodByExceptionType(exception.getClass()); // 其次,获取不到,则使用异常 cause 对应的方法 if (method == null) { Throwable cause = exception.getCause(); if (cause != null) { method = resolveMethodByExceptionType(cause.getClass()); } } return method; } ``` - 按照 `exception` 和 `exception.cause` 的先后,调用 `#resolveMethodByExceptionType(Class exceptionType)` 方法,获得异常对应的方法。代码如下: ``` // ExceptionHandlerMethodResolver.java @Nullable public Method resolveMethodByExceptionType(Class 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 exceptionType)` 方法,获得异常对应的方法。代码如下: ``` // ExceptionHandlerMethodResolver.java @Nullable private Method getMappedMethod(Class exceptionType) { List> matches = new ArrayList<>(); // 遍历 mappedMethods 数组,匹配异常,添加到 matches 中 for (Class 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` 比较器,胖友自己点击 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/core/ExceptionDepthComparator.java) 查看。大体的逻辑是,比较它们和目标类的**继承层级**,越小越匹配。 ## 7.4 getExceptionHandlerMethod `#getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception)` 方法,获得异常对应的 ServletInvocableHandlerMethod 对象。代码如下: ``` // ExceptionHandlerMethodResolver.java @Nullable protected ServletInvocableHandlerMethod getExceptionHandlerMethod( @Nullable HandlerMethod handlerMethod, Exception exception) { // 处理器的类型 Class handlerType = null; // <1> 首先,如果 handlerMethod 非空,则先获得 Controller 对应的 @ExceptionHandler 处理器对应的方法 if (handlerMethod != null) { // Local exception handler methods on the controller class itself. // To be invoked through the proxy, even in case of an interface-based proxy. // 获得 handlerType handlerType = handlerMethod.getBeanType(); // 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象 ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); if (resolver == null) { resolver = new ExceptionHandlerMethodResolver(handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } // 获得异常对应的 Method 方法 Method method = resolver.resolveMethod(exception); // 如果获得到 Method 方法,则创建 ServletInvocableHandlerMethod 对象,并返回 if (method != null) { return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); } // For advice applicability check below (involving base packages, assignable types // and annotation presence), use target class instead of interface-based proxy. // 获得 handlerType 的原始类。因为,此处有可能是代理对象 if (Proxy.isProxyClass(handlerType)) { handlerType = AopUtils.getTargetClass(handlerMethod.getBean()); } } // <2> 其次,使用 ControllerAdvice 对应的 @ExceptionHandler 处理器对应的方法 for (Map.Entry entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); // 如果 ControllerAdvice 支持当前的 handlerType if (advice.isApplicableToBeanType(handlerType)) { // 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象 ExceptionHandlerMethodResolver resolver = entry.getValue(); // 获得异常对应的 Method 方法 Method method = resolver.resolveMethod(exception); // 如果获得到 Method 方法,则创建 ServletInvocableHandlerMethod 对象,并返回 if (method != null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } // 最差,获取不到 return null; } ``` - 虽然代码比较多,但是总体分成 `<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 @Override @Nullable protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { // <1> 获得异常对应的 ServletInvocableHandlerMethod 对象 ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); if (exceptionHandlerMethod == null) { return null; } // <1.1> 设置 ServletInvocableHandlerMethod 对象的相关属性 if (this.argumentResolvers != null) { exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } // <1.2> 创建 ServletWebRequest 对象 ServletWebRequest webRequest = new ServletWebRequest(request, response); // <1.3> 创建 ModelAndViewContainer 对象 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); try { if (logger.isDebugEnabled()) { logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod); } // <2> 执行 ServletInvocableHandlerMethod 的调用 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) { // <2.1> 发生异常,则直接返回 // Any other than the original exception is unintended here, // probably an accident (e.g. failed assertion or the like). if (invocationEx != exception && logger.isWarnEnabled()) { logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx); } // Continue with default processing of the original exception... return null; } // <3.1> 如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。 if (mavContainer.isRequestHandled()) { return new ModelAndView(); // <3.2> 如果 mavContainer 未处,则基于 `mavContainer` 生成 ModelAndView 对象 } else { ModelMap model = mavContainer.getModel(); HttpStatus status = mavContainer.getStatus(); // <3.2.1> 创建 ModelAndView 对象,并设置相关属性 ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status); mav.setViewName(mavContainer.getViewName()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } // <3.2.2> TODO 1004 flashMapManager if (model instanceof RedirectAttributes) { Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; } } ``` - ``` <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》](http://svip.iocoder.cn/Spring-MVC/HandlerAdapter-2-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 @Nullable private MessageSource messageSource; ``` ## 8.2 applyStatusAndReason `#applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)` 方法,设置错误响应。代码如下: ``` // ResponseStatusExceptionResolver.java protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response) throws IOException { // 情况一,如果无错误提示,则响应只设置状态码 if (!StringUtils.hasLength(reason)) { response.sendError(statusCode); // 情况二,如果有错误信息,则响应设置状态码 + 错误提示 } else { // 进一步解析错误提示,如果有 messageSource 的情况下 String resolvedReason = (this.messageSource != null ? this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) : reason); // 设置 response.sendError(statusCode, resolvedReason); } // 创建“空” ModelAndView 对象,并返回 return new ModelAndView(); } ``` - 注意,此处返回的也是“空”的 ModelAndView 对象。 ## 8.3 doResolveException 实现 `#doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法,代码如下: ``` // ResponseStatusExceptionResolver.java @Override @Nullable protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { try { // <1> 情况一,如果异常是 ResponseStatusException 类型,进行解析并设置到响应 if (ex instanceof ResponseStatusException) { return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler); } // <2> 情况二,如果有 @ResponseStatus 注解,进行解析并设置到响应 ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class); if (status != null) { return resolveResponseStatus(status, request, response, handler, ex); } // <3> 情况三,使用异常的 cause 在走一次情况一、情况二的逻辑。 if (ex.getCause() instanceof Exception) { ex = (Exception) ex.getCause(); return doResolveException(request, response, handler, ex); } } catch (Exception resolveEx) { logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx); } return null; } ``` - 分成三种情况。 - `<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 @Override @Nullable protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { try { if (ex instanceof HttpRequestMethodNotSupportedException) { return handleHttpRequestMethodNotSupported( (HttpRequestMethodNotSupportedException) ex, request, response, handler); } else if (ex instanceof HttpMediaTypeNotSupportedException) { return handleHttpMediaTypeNotSupported( (HttpMediaTypeNotSupportedException) ex, request, response, handler); } else if (ex instanceof HttpMediaTypeNotAcceptableException) { return handleHttpMediaTypeNotAcceptable( (HttpMediaTypeNotAcceptableException) ex, request, response, handler); } else if (ex instanceof MissingPathVariableException) { return handleMissingPathVariable( (MissingPathVariableException) ex, request, response, handler); } else if (ex instanceof MissingServletRequestParameterException) { return handleMissingServletRequestParameter( (MissingServletRequestParameterException) ex, request, response, handler); } else if (ex instanceof ServletRequestBindingException) { return handleServletRequestBindingException( (ServletRequestBindingException) ex, request, response, handler); } else if (ex instanceof ConversionNotSupportedException) { return handleConversionNotSupported( (ConversionNotSupportedException) ex, request, response, handler); } else if (ex instanceof TypeMismatchException) { return handleTypeMismatch( (TypeMismatchException) ex, request, response, handler); } else if (ex instanceof HttpMessageNotReadableException) { return handleHttpMessageNotReadable( (HttpMessageNotReadableException) ex, request, response, handler); } else if (ex instanceof HttpMessageNotWritableException) { return handleHttpMessageNotWritable( (HttpMessageNotWritableException) ex, request, response, handler); } else if (ex instanceof MethodArgumentNotValidException) { return handleMethodArgumentNotValidException( (MethodArgumentNotValidException) ex, request, response, handler); } else if (ex instanceof MissingServletRequestPartException) { return handleMissingServletRequestPartException( (MissingServletRequestPartException) ex, request, response, handler); } else if (ex instanceof BindException) { return handleBindException((BindException) ex, request, response, handler); } else if (ex instanceof NoHandlerFoundException) { return handleNoHandlerFoundException( (NoHandlerFoundException) ex, request, response, handler); } else if (ex instanceof AsyncRequestTimeoutException) { return handleAsyncRequestTimeoutException( (AsyncRequestTimeoutException) ex, request, response, handler); } } catch (Exception handlerEx) { if (logger.isWarnEnabled()) { logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx); } } return null; } ``` 比较简单,就不啰嗦解析。感兴趣的胖友,自己去瞅瞅这个类即可。 # 10. SimpleMappingExceptionResolver > 艿艿:这个类是选读的,不敢兴趣的胖友,可以绕过。 `org.springframework.web.servlet.handler.SimpleMappingExceptionResolver` ,继承 AbstractHandlerExceptionResolver 抽象类,是 Spring MVC 提供的一个简易匹配的异常处理方式。 可通过 XML 中进行配置,示例如下: ``` businessEx ``` ## 10.1 构造方法 ``` // SimpleMappingExceptionResolver.java /** The default name of the exception attribute: "exception". */ public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception"; /** * 异常的视图映射 * * KEY:异常的全类名 * VALUE:视图名 */ @Nullable private Properties exceptionMappings; /** * 排除的异常的数组 */ @Nullable private Class[] excludedExceptions; /** * 默认视图名 */ @Nullable private String defaultErrorView; /** * 默认的状态码 */ @Nullable private Integer defaultStatusCode; /** * 状态码的映射 * * KEY:视图名 * VALUE:状态码 */ private Map statusCodes = new HashMap<>(); /** * 异常设置到 {@link ModelAndView} 的属性名 */ @Nullable private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE; ``` - 属性比较多,随着下面的方法,一起瞅瞅。 ## 10.2 doResolveException 实现 `#doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法,代码如下: ``` // SimpleMappingExceptionResolver.java @Override @Nullable protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { // Expose ModelAndView for chosen error view. // <1> 获得异常对应的视图 String viewName = determineViewName(ex, request); if (viewName != null) { // Apply HTTP status code for error views, if specified. // Only apply it if we're processing a top-level request. // <2> 获得视图对应的状态码 Integer statusCode = determineStatusCode(request, viewName); // <3> 设置状态码到响应 if (statusCode != null) { applyStatusCodeIfPossible(request, response, statusCode); } // <4> 创建 ModelAndView 对象,并返回 return getModelAndView(viewName, ex, request); } else { return null; } } ``` - `<1>` 处,调用 `#determineViewName(Exception ex, HttpServletRequest request)` 方法,获得异常对应的视图。代码如下: ``` // SimpleMappingExceptionResolver.java @Nullable 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 @Nullable 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 @Nullable 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. 彩蛋 虽然很长,但是实际上,灰常简单。嘿嘿。 参考和推荐如下文章: - 韩路彪 [《看透 Spring MVC:源代码分析与实践》](https://item.jd.com/11807414.html) 的 [「第16章 HandlerExceptionResolver」](https://svip.iocoder.cn/Spring-MVC/HandlerExceptionResolver/#) 小节