上一部分,FrameworkServlet 将 onRefresh 留给了 DispatcherServlet 来完成,
所以进入 DispatcherServlet # onRefresh
刷新Spring容器(这里刷新的是子容器)
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context); //这里的参数context,即是之前创建的SpringWeb容器(之后又作为参数,传递给了每个组件的初始化函数)
}
初始化九大组件:initStrategies()
初始化了以下9个组件。SpringMVC正是依赖它们来完成的整个请求处理流程。在后面会详细解析
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
DispatcherServlet 中的重要组件:
private MultipartResolver multipartResolver; //文件上传
private LocaleResolver localeResolver;
private ThemeResolver themeResolver;
private List<HandlerMapping> handlerMappings; //处理器映射器
private List<HandlerAdapter> handlerAdapters; //处理器适配器
private List<HandlerExceptionResolver> handlerExceptionResolvers; //异常处理器
private RequestToViewNameTranslator viewNameTranslator;
private FlashMapManager flashMapManager; //快照
private List<ViewResolver> viewResolvers; //视图解析器
上面的组件,它们十分重要,是整个 SpringMVC 工作的核心。这里先介绍它们的初始化(怎么被设置值的),后续再分析每个组件的是负责干啥的。
举例:处理器映射器 HandlerMapping 组件的初始化:initHandlerMappings()
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 这个if-else的逻辑:是从 SpringWeb 容器中查找 HandlerMapping ,包括在父容器中的也会查找。
// 如果,我们在xml中配置了一个HandlerMapping,或者配置了<mvc:annotation-driven/>,那么就会在容器中找到。不再进入后面的 "默认策略" 逻辑
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); //getBean()方法的逻辑,会从父容器中找....
this.handlerMappings = Collections.singletonList(hm);
}//...
}
// 如果没有找到,则使用默认注册的 HandlerMapping (至少要确保有一个HandlerMapping能拿来用)
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
//...
}
}
默认的组件注册策略(默认配置,策略模式):(相当于一个适配方法:initHandlerMappings()、initHandlerAdapters()、…都会被适配到此处)
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName(); // key 为 HandlerMapping.clss(传过来的也可能是:HandlerAdapter.class、ViewResolver.class、...)
String value = defaultStrategies.getProperty(key); // 从外部 defaultStrategies 处获取
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); // 创建 HandlerMapping 对象
strategies.add((T) strategy); // 加入集合
}//...
}
return strategies;
}
else {
return new LinkedList<>();
}
}
此处的 defaultStrategies 由外部properties文件所决定,defaultStrategies 在 DispatcherServlet 类的静态代码块中被赋值:
private static final Properties defaultStrategies; static { // 从properties文件中载入默认的初始化接口(开发人员不能自定义properties文件) try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); // 从"DispatcherServlet.properties"处加载 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); }//... }
DispatcherServlet.properties 文件内容(文件位于源码 DispatcherServlet.class 同级目录下)
# 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
可以看到,这里确实定义了不同组件的类型,一共定义了8个组件,处理上传组件MutpartResolver是没有默认配置的,这也很容易理解,并不是每个应用都需要上传功能,即使需要上传也不一定就要使用MultipartResolver,所以MultipartResolver不需要默认配置。另外HandlerMapping、HandlerAdapter和HandlerExceptionResolver都配置了多个,其实View-Resolver也可以有多个,只是默认的配置只有一个。
这里需要注意的是:
默认配置并不是最优配置,也不是spring的推荐配置,只是在没有配置的时候可以有个默认值,不至于空着。
默认配置是在相应的组件没有在springmvc.xml中配置时,才会使用。例如,我们在springmvc.xml中配置了HandlerMapping组件:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
那么就会使用这个 BeanNameUrlHandlerMapping 来作为HandlerMapping组件,默认的配置不再生效。(不再会有默认配置中的RequestMappingHandlerMapping了)
另外,当使用了 “< mvc:annotation-driven/>” 后,也并不会全部使用默认配置。因为它配置了HandlerMapping、HandlerAdapter和Handler-ExceptionResolver,而且还做了很多别的工作,更详细的内容可以查看 org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser( 底层是向Spring容器中添加BeanDefinition )
总结:
通过此阶段,DispatcherServlet 初始化了内部的9个组件(若没有在容器中配置这些组件,则会默认从外部文件 “DispatcherServlet.properties” 处决定初始化策略)
DispatcherServlet 在后续请求处理的过程中,会频繁依赖这些组件来完成整个请求服务。
参考书籍《看透 SpringMVC 源代码分析与实践》