1443 lines
56 KiB
Markdown
1443 lines
56 KiB
Markdown
# 精尽 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 的类图如下:
|
||
|
||
[](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<HandlerExceptionResolver> 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<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 类
|
||
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<HandlerExceptionResolver> resolvers;
|
||
|
||
/**
|
||
* 优先级,最低
|
||
*/
|
||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||
```
|
||
|
||
那么,还是让我们来看看,在**默认配置的 Spring Boot 场景**下,是通过 WebMvcConfigurationSupport 的 `#handlerExceptionResolver()` 方法,进行初始化。代码如下:
|
||
|
||
```
|
||
// WebMvcConfigurationSupport.java
|
||
|
||
@Bean
|
||
public HandlerExceptionResolver handlerExceptionResolver() {
|
||
// <1> 创建 HandlerExceptionResolver 数组
|
||
List<HandlerExceptionResolver> 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<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` 中。
|
||
|
||
- `<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) {
|
||
// <x> 获得真正的 handler
|
||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||
handler = handlerMethod.getBean();
|
||
// 调用父方法
|
||
return super.shouldApplyTo(request, handler);
|
||
// 情况三,直接返回 false
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
```
|
||
|
||
- 重点在于情况二,需要在 `<x>` 处,调用 `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<HandlerMethodArgumentResolver> customArgumentResolvers;
|
||
|
||
@Nullable
|
||
private HandlerMethodArgumentResolverComposite argumentResolvers;
|
||
|
||
@Nullable
|
||
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
|
||
|
||
@Nullable
|
||
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
|
||
|
||
private List<HttpMessageConverter<?>> messageConverters;
|
||
|
||
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
|
||
|
||
private final List<Object> responseBodyAdvice = new ArrayList<>();
|
||
|
||
@Nullable
|
||
private ApplicationContext applicationContext;
|
||
|
||
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
|
||
new ConcurrentHashMap<>(64);
|
||
|
||
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> 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<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
|
||
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
|
||
}
|
||
// 初始化 returnValueHandlers 参数
|
||
if (this.returnValueHandlers == null) {
|
||
List<HandlerMethodReturnValueHandler> 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<ControllerAdviceBean> 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<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
|
||
|
||
/**
|
||
* 已经匹配的方法
|
||
*
|
||
* 在 {@link #resolveMethod(Exception)} 方法中初始化
|
||
*/
|
||
private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);
|
||
|
||
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
|
||
// <1> 遍历 @ExceptionHandler 注解的方法
|
||
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
|
||
// <2> 遍历处理的异常集合
|
||
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
|
||
// <3> 添加到 mappedMethods 中
|
||
addExceptionMapping(exceptionType, method);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
- `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
|
||
|
||
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<? extends Throwable> exceptionType)` 方法,获得异常对应的方法。代码如下:
|
||
|
||
```
|
||
// ExceptionHandlerMethodResolver.java
|
||
|
||
@Nullable
|
||
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
|
||
|
||
@Nullable
|
||
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` 比较器,胖友自己点击 [传送门](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<ControllerAdviceBean, ExceptionHandlerMethodResolver> 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<String, ?> 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 中进行配置,示例如下:
|
||
|
||
```
|
||
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
|
||
<!-- 默认异常视图 -->
|
||
<property name="defaultErrorView" value="error" />
|
||
<!-- 视图中获取exception信息变量名 -->
|
||
<property name="exceptionAttribute" value="ex" />
|
||
<!-- 异常同视图映射关系 -->
|
||
<property name="exceptionMappings">
|
||
<props>
|
||
<prop key="com.lcifn.springmvc.exception.BusinessException">businessEx</prop>
|
||
</props>
|
||
</property>
|
||
</bean>
|
||
```
|
||
|
||
## 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<String, Integer> 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/#) 小节 |