基本概念
在配置 SpringMVC 的 Servlet 时,实际定义的就是 DispatcherServlet 类,它是 FrameworkServlet 的子类,具体的继承关系如下:
DispatcherServlet 重写了父类的 onRefresh 方法,说明它也负责 SpringMVC 的初始化工作。
原理分析
首先来看该类的 onRefresh 方法,发现真正的初始化过程在 initStrategies 中定义。
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 初始化多文件上传解析器
initMultipartResolver(context);
// 初始化区域解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化处理器映射集
initHandlerMappings(context);
// 初始化处理器适配器
initHandlerAdapters(context);
// 初始化处理器异常解析器
initHandlerExceptionResolvers(context);
// 初始化请求到视图名转换器
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
// 初始化 flash 映射管理器
initFlashMapManager(context);
}
观察代码,可以发现它负责初始化 SpringMVC 的各种组件。
需要注意的是,唯一入参 context 就是在其父类初始化过程创建的 SpringMVC 容器。
默认策略
1.作用
在 SrpingMVC 中存在一个默认的属性文件:DispatcherServlet.properties。它的 key 是接口名,value 是实现类的名称。具体内容如下:
它被称作默认策略、默认配置。作用是:
- 当 SrpingMVC 中在初始化组件时,若在容器中找不到定义的相关组件,则从默认策略中获取。
2.初始化
默认策略的初始化过程在 DispatcherServlet 静态域中定义,它最终被加载到成员变量 defaultStrategies :
private static final Properties defaultStrategies;
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
static {
try {
// 加载默认策略的属性文件
ClassPathResource resource =
new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}catch (IOException ex) {
// 抛出异常...
}
}
3.调用过程
在该类中定义了两种获取默认策略的方式:
- 获取同种类型的一个默认组件:getDefaultStrategy。
- 获取同种类型的所有默认组件:getDefaultStrategies。
- getDefaultStrategy
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
// 关键 -> 具体实现还是得通过 getDefaultStrategies
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
// 抛出异常...
}
return strategies.get(0);
}
- getDefaultStrategies
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 取得默认策略(属性文件)的 key、value
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
// 拆分成数组,可能一个接口存在多个实现类,即 value 中包含多个类
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<T>(classNames.length);
// 遍历数组
for (String className : classNames) {
try {
// 取得类对象
Class<?> clazz =
ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 在 SrpingMVC 容器中创建 Bean
Object strategy = createDefaultStrategy(context, clazz);
// 将创建的 Bean 添加进集合
strategies.add((T) strategy);
}catch (ClassNotFoundException ex) {
// 抛出异常...
}catch (LinkageError err) {
// 抛出异常...
}
}
return strategies;
}else {
return new LinkedList<T>();
}
}
流程探究
由于在 DispatcherServlet 中初始化各种组件的流程大致相同:
- 从 SpringMVC 容器中手动获取指定名称的 Bean;不存在则置为空。
- 从 SpringMVC 容器中手动获取指定名称的 Bean;不存在则从默认策略中查找相同类型的 Bean。
- 从 SpringMVC 容器中手动获取指定名称的 Bean , 或从所有容器中获取相同类型的 Bean ;不存在则从默认策略中查找相同类型的 Bean。
因此这里只分析 initHandlerMappings 方法,它是上述流程的第三种情况。来看源码:
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
private List<HandlerMapping> handlerMappings;
// 表示是否检测所有容器中的 HandlerMapping
private boolean detectAllHandlerMappings = true;
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 1.1.检测所有所有容器,返回指定类型和子类型的所有 Bean
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
HandlerMapping.class, true, false);
// 不为空则添加进集合,并排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings =
new ArrayList<HandlerMapping>(matchingBeans.values());
OrderComparator.sort(this.handlerMappings);
}
}else {
try {
// 1.2.从 SpringMVC 容器中手动获取指定的 Bean,并添加集合
HandlerMapping hm =
context.getBean(HANDLER_MAPPING_BEAN_NAME,HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}catch (NoSuchBeanDefinitionException ex) { }
}
// 2.从默认策略中获取相同类型的 Bean
if (this.handlerMappings == null) {
this.handlerMappings =
getDefaultStrategies(context, HandlerMapping.class);
// 省略部分代码...
}
}
- BeanFactoryUtils.beansOfTypeIncludingAncestors
public static <T> Map<String, T> beansOfTypeIncludingAncestors(
ListableBeanFactory lbf, Class<T> type,
boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
// 省略部分代码...
Map<String, T> result = new LinkedHashMap<String, T>(4);
// 1.在当前容器寻找指定类型的bean
result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
// 2.在它的所有父容器寻找指定类型的bean,
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
// 关键 -> 再次调用该方法,会递归查询,直到找到顶层的容器为止
Map<String, T> parentResult =
beansOfTypeIncludingAncestors(
(ListableBeanFactory)hbf.getParentBeanFactory(),type,includeNonSingletons, allowEagerInit);
// 过滤与 result 中相同名称的 Bean
for (Map.Entry<String, T> entry : parentResult.entrySet()) {
String beanName = entry.getKey();
if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
result.put(beanName, entry.getValue());
}
}
}
}
return result;
}
总结
DispatcherServlet 初始化的工作就是负责初始化各种不同的组件。它好比给 SpringMVC 添加功能模块,一旦功能模块添加完毕,SpringMVC 就可以正常工作。因此 SpringMVC 的初始化工作也到此结束。