ExceptionHandlerExceptionResolver源码分析
与其说是分析异常处理器源码,倒不如是把Mvc自动配置走了一遍。
ExceptionHandlerExceptionResolver、RequestMappingHandlerMapping、RequestMappingHandlerAdapter的配置流程都是相似的。
自动配置
在WebMvcConfigurationSupport中,该类是Springboot自动配置Mvc最核心的类,完成了RequestHandlerMapping、Adapter、异常处理器、字符过滤器等等SSM中需要手动配置的部分,通常情况下,我们不需要自己直接配置该类,Mvc提供了MvcConfiure帮助我们自定义。
在AutoConfiguration中
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
可以看到,一旦我们向容器中注入了WebMvcConfigrationSupport,那我们又回到了手动配置Mvc的时候。
事实上,Springboot的AutoConfigration主要工作由EnableWebMvcConfiguration完成,
该类继承自WebMvcConfigurationSupport,同时使用委托模式使Mvc配置可自义,具体可查看DelegatingWebMvcConfiguration。
@Bean
public HandlerExceptionResolver handlerExceptionResolver(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
/* 1. 自定义config */
configureHandlerExceptionResolvers(exceptionResolvers);
/* 2. 默认配置 */
if (exceptionResolvers.isEmpty()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
}
/* 3.扩展 */
extendHandlerExceptionResolvers(exceptionResolvers);
/* 4. 委托, */
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
- 优先自定义整个exceptionResolvers,这里会调用到我们编写的MvcConfigure配置类,如果结果不为空,SpringMvc认为我们有自定义配置,不再使用默认配置。
- 默认配置,会添加前面提到的三个Resolver。
- 扩展,这里会调用我们MvcConfigure的extendExption..方法
- 将三个Resolver委托。
addDefaultHandlerExceptionResolvers
/* 1. 此处支持自定义 */
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
/* 2. 以下的getXXX方法都会使用到MvcConfigure中自定义的方法 */
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 responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
- 会优先委托WebMvcRegistrations实现,如果返回null,则使用默认
- 前面说过,Mvc自动配置依赖于EnableWebMvcConfiguration,这些getXXX方法都会被委托,添加自定义配置
异常处理器的初始化过程
扫描了所有ControllerAdvice注解的类,从该类扫描@ExceptionHandler注解,保存异常及对应的处理方法
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
/* 真正解决异常的resolver */
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
/* 如果在其中找到处理异常的方法 */
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
在ExceptionHandlerMethodResolver初始化方法中
handlerType即我们自定义全局异常处理器,扫描该全局处理器的所有被@ExceptionHandler注解的方法及其匹配的异常,并保存。
之后只需依据发生的异常,匹配到对应的方法即可。
匹配的时候,如果目标异常没有对应的方法,则尝试该异常的cause,如果没有,返回null
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
addExceptionMapping(exceptionType, method);
}
}
}
自定义配置异常处理器
ExceptionHandlerExceptionResolver部分属性
...
@Nullable
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
@Nullable
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
private List<HttpMessageConverter<?>> messageConverters;
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
private final List<Object> responseBodyAdvice = new ArrayList<>();
...
-
自定义WebMvcConfigSupport,不推荐,需要配置整个Mvc
-
通过提供扩展接口
- 参数解析器、返回值解析器、消息转换器、contentNegotiationManager,可通过MvcConfigure
- responseBodyAdvice,通过ControllerAdive注解
上两者都支持通过WebMvcRegistrations配置
RequestMappingHandlerMapping、RequestMappingHandlerAdapter配置方法也差不多。
Q.E.D.