SpringMVC源码 3.2 DispatchServlet 组件初始化
DispatchServlet还剩下最后的刷新,onRefresh方法。在这个方法中主要初始了一些DispatchServlet中需要使用的一些组件。
例如各种Resolver、HandlerMapping、HandlerAdapter、HandlerExceptionResolvers等。
onRefresh()方法是FramworkServlet提供的模板方法,在子类DispatchServlet中进行了重写,主要用于刷新Spring在web功能实现中所必须提供的全局变量。9个必要组件。
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
(1)初始化MultipartResolver
在Spring中MultipartResolver是用来处理上传文件的。
默认情况下,Spring是没有Multipart处理的。如果想使用Spring的multipart,则需要在上下文中添加multipart解析器,这样每个请求都会被检查是否包含multipart,如果请求中有multipart,那么上下文中的MultipartResolver就会处理他。常用配置:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maximumFileSize"><value>100000</value></property>
</bean>
CommonsMultipartResolver还提供了其他功能帮助完成上传文件
首先通过context.getBean在容器中按注册时的名称和类型进行查找。这里是“multipartResolver”名称或者MultipartResolver.class类型。所以需要在SpringMVC的配置文件中添加相应类型的组件,容器就能自动找到。如果找不则为null。
注意:这边的context是FrameworkServlet中创建的WebApplicationContext,不是在ContextLoader中创建的。
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
}
}catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
"': no multipart request handling provided");
}
}
}
(1)初始化LocaleResolver
首先通过context.getBean在容器中按注册时的名称和类型进行查找。这里是“localeResolver”名称或者LocaleResolver.class类型。
如果在容器中找不到,这通过getDefaultStrategy(context, LocaleResolver.class);获取默认的组件。
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
}
}catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver + "]");
}
}
}
如何获取默认的组件
调用了getDefaultStrategies方法,返回的是个List。List的size必须是1,并取第一个返回值。
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
通过Class为key,从strategyInterface获取对应的value。value值包含了各个初始化组件类的默认值。通过反创建出对应的实例。
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
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());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}catch (ClassNotFoundException ex) {
.....
}catch (LinkageError err) {
.....
}
}
return strategies;
}else {
return new LinkedList<T>();
}
}
存在一段static代码块,用于加载defaultStrategies。读取DispatchServlet同级目录(org.springframework.web.servlet)下的DispatcherServlet.properties的文件。
static {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
可以看到这里定义了不同组件的类型,一共定义了8个。因为MultipartResolver是不需要的,默认返回为null。因为并不是每个应用都是需要上传文件组件的。
注意默认配置并不是最优配置,也不是spring的推荐配置。只是在没有配置的时候有个默认值,不至于空着。
对于MultipartResolver,LocaleResolver,ThemeResolver,RequestToViewNameTranslator,FlashMapManager这五个组件,只需要一个类。处理逻辑也很简单,首先从context.getBean能不能获取,不能获取则使用DispatcherServlet.properties中的默认值。
但是对于另外四个组件,逻辑就比较复杂,会判断detectAll-----值是否为true,如果是true则从所有的相关的定义类(在beanDefinitionName变量中保存)。如果为false在从context.getBean。在获取不到则使用默认值。具体在后面介绍
# 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.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
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
(3)初始化ThemeResolver
(4)初始化HandlerMappings
客户端发出Requet时DispatchServlet会将Request提交给HandlerMapping,然后HandlerMapping根据WebApplicationContext的配置回传给DispatchServlet相应的Controller。我们可以为DispatchServlet提供给多个HandlerMapping使用。我们会将制定的HandlerMapping的优先级进行排序,DispatchServlet选用过程中会使用优先级高的。如果当前的HandlerMapping能返回可用的Handler,那DispatchServlet就使用返回的Handler处理web请求,不再询问其他的HandlerMapping。如果不行则询问下一个HandlerMapping,直到获取一个可用的Handler。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
默认情况下,会加载当前系统中所有实现HandlerMapping接口的bean。(其实也不是全部)。matchingBeans返回的是三个HandlerMapping。
org.springframework.web.servlet.handler.
BeanNameUrlHandlerMapping
@45451b28
org.springframework.web.servlet.handler.
SimpleUrlHandlerMapping
@223a93da
org.springframework.web.servlet.mvc.method.annotation.
RequestMappingHandlerMapping
@7f957ed8(替换了原来的
DefaultAnnotationHandlerMapping
)
从哪里确定就是这三个bean那,会在beanDefinitionNames中去匹配。
只希望SpringMVC加载指定的HandlerMapping,可以修改web.xml中DispatchServlet的初始化参数,将detectAllHandlerMappings设置为false。此时SpringMVC会查找名为“HandlerMapping”的bean,作为当前系统的唯一HandlerMapping。如果没有在spring-serlvet.xml配置文件中定义handlerMapping的话,就会使用DispatchServlet.properties文件中的默认值。
<init-param>
<param-name>detectAllHandlerMappings</param-name>
<param-value>false</param-value>
</init-param>
(5)初始化HandlerAdapters
初始化HanlerAdapter的逻辑和HandlerMapping的逻辑差不多。对应的值变成了detectAllHandlerAdapter。
全部的HandlerAdapter也变成了下面这三个。
org.springframework.web.servlet.mvc.
HttpRequestHandlerAdapter
@6c45f317
org.springframework.web.servlet.mvc.
SimpleControllerHandlerAdapter
@1a971906
org.springframework.web.servlet.mvc.method.annotation.
RequestMappingHandlerAdapter
@7766dd27
剩下几个的初始化都和以上的差不多,只是每个组件的功能不同。不同组件的具体功能以后再写。
(6)初始化HandlerExceptionResolvers
(7)初始化RequestToViewNameTranslator
(8)初始化ViewResolvers
(9)初始化FlashManager