本文所用源代码基于
Spring Web MVC 5.1.4.RELEASE
概述
DispatcherServlet#initHandlerExceptionResolvers
方法用于准备DispatcherServlet
处理请求时所使用的HandlerExceptionResolver
对象(可以有多个)。这些HandlerExceptionResolver
对象获取之后记录在DispatcherServlet
的实例成员属性handlerExceptionResolvers
中。
DispatcherServlet
另有一个实例成员属性detectAllHandlerExceptionResolvers
,该属性缺省为true
。
DispatcherServlet
准备HandlerExceptionResolver
的流程如下 :
-
从容器获取
HandlerExceptionResolver
对象;- 当
detectAllHandlerExceptionResolvers
为true
时,从容器(以及祖先容器)获取所有类型为HandlerExceptionResolver
的bean
组件,记录到handlerExceptionResolvers
并排序; - 当
detectAllHandlerExceptionResolvers
为false
时,从容器(以及祖先容器)获取名称为handlerExceptionResolver
的bean
组件,记录到handlerExceptionResolvers
,这种情况下handlerExceptionResolvers
中最多有一个元素;
- 当
-
如果上面步骤结束时
handlerExceptionResolvers
为空则创建缺省HandlerExceptionResolver
对象记录到handlerExceptionResolvers
;
经过上面的流程,我们可以看出:
DispatcherServlet
的属性handlerExceptionResolvers
中总是会有至少一个可供使用的HandlerExceptionResolver
对象。- 如果想定制
DispatcherServlet
所使用的HandlerExceptionResolver
,开发人员可以注册相应的HandlerExceptionResolver
对象到容器; - 如果想指定
DispatcherServlet
为某一个HandlerExceptionResolver
,开发人员可以注册自定义的HandlerExceptionResolver
到容器,但必须使用名称handlerExceptionResolver
,并且需要先设置DispatcherServlet
的属性detectAllHandlerExceptionResolvers
为false
。
到这里,你可能会有疑问,缺省情况下,开发人员并没有往容器中定义
HandlerExceptionResolver
组件,但实际上DispatcherServlet
能够从容器获得HandlerExceptionResolver
组件,这又是为什么呢?关于这一点,请参考 :
源代码解析
方法 #initHandlerExceptionResolvers
/**
* Initialize the HandlerExceptionResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to no exception resolver.
*/
private void initHandlerExceptionResolvers(ApplicationContext context) {
// 初始化记录 handlerExceptionResolvers 对象的属性变量为null
this.handlerExceptionResolvers = null;
// 根据属性 detectAllHandlerExceptionResolvers 决定是检测所有的 HandlerExceptionResolver 对象,还是
// 使用指定名称的 HandlerExceptionResolver 对象
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.
// 排序,关于这里的排序,可以参考 WebMvcConfigurationSupport 类中对各种
// HandlerExceptionResolver bean 进行定义时所使用的 order 属性,顺序属性很关键,
// 因为它涉及到 HandlerExceptionResolver 使用时的优先级
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
// 获取名称为 handlerExceptionResolver 的 HandlerExceptionResolver bean
// 并记录到 handlerExceptionResolvers
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.
}
}
// 如果上面步骤从容器获取 HandlerExceptionResolver 失败,则使用缺省策略创建
// HandlerExceptionResolver 对象记录到 handlerExceptionResolvers
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
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");
}
}
}
方法 #getDefaultStrategies
该方法使用指定的策略接口 strategyInterface
创建一组策略对象。上面的方法initHandlerExceptionResolvers
就是使用该方法创建了一组缺省的HandlerExceptionResolver
策略对象。
该方法会使用DispatcherServlet
缺省配置文件DispatcherServlet.properties
获取缺省要使用的策略实现类。
/**
* Create a List of default strategy objects for the given strategy interface.
* The default implementation uses the "DispatcherServlet.properties" file (in the same
* package as the DispatcherServlet class) to determine the class names. It instantiates
* the strategy objects through the context's BeanFactory.
* @param context the current WebApplicationContext
* @param strategyInterface the strategy interface
* @return the List of corresponding strategy objects
*/
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 策略接口长名称作为 key
String key = strategyInterface.getName();
// 这里 defaultStrategies 是一个类静态属性,指向classpath resource 文件 DispatcherServlet.properties
// 该行获取策略接口对应的实现类,是','分割的实现类的长名称
String value = defaultStrategies.getProperty(key);
if (value != null) {
//
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
// 获取策略接口实现类
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 创建该策略接口实现类的对象
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
方法 #createDefaultStrategy
该方法使用AutowireCapableBeanFactory#createBean
创建策略实现类的一个实例对象。
/**
* Create a default strategy.
* The default implementation uses
* org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean.
* @param context the current WebApplicationContext
* @param clazz the strategy implementation class to instantiate
* @return the fully configured strategy instance
* @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
*/
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
文件DispatcherServlet.properties
文件DispatcherServlet.properties
是一个属性文件。每个属性的key
是一个策略接口的长名称,而value
是key
指定的策略接口的多个实现类的长名称,每个类名称之间使用,
分割。
该文件位于包org.springframework.web.servlet
,和DispatcherServlet
在同一个pacakge
路径下面。具体内容如下 :
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager