DispatcherServlet
通过源码我们可以看到,onRefresh方法是DispatcherServlet的入口方法。onRefresh中简单地调用了initStrategies,在initStrategies中调用了9个初始化方法。
// org.springframework.web.servlet.DispatcherServletprotected 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); initFlashMapManager(context);}
我们会想:为什么不将initStrategies的具体实现直接写到onRefresh中呢?initStrategies方法不是多余的吗?
其实这主要是分层的原因,onRefresh是用来刷新容器的,initStrategies用来初始化一些策略组件。如果把initStrategies里面的代码直接写到onRefresh里面,对于程序的运行也没有影响,不过这样一来,如果在onRefresh中想再添加别的功能,就会没有将其单独写一个方法出来逻辑清晰,不过这并不是最重要的,更重要的是,如果在别的地方也需要调用initStrategies方法(如需要修改一些策略后进行热部署),但initStrategies没独立出来,就只能调用onRefresh,那样在onRefresh增加了新功能的时候就麻烦了。另外单独将initStrategies写出来还可以被子类覆盖,使用新的模式进行初始化。
initStrategies的具体内容非常简单,就是初始化的9个组件,下面以LocaleResolver为例来分析具体的初始化方式://初始化区域解析器// org.springframework.web.servlet.DispatcherServletprivate void initLocaleResolver(ApplicationContext context) { try { // 在context中获取 this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); } }catch (NoSuchBeanDefinitionException ex) { // 使用默认策略 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 + "]"); } }}
初始化方式分两步:首先通过context.getBean在容器里面按注册时的名称或类型(这里指“localeResolver”名称或者LocaleResolver.class类型)进行查找,所以在Spring MVC的配置文件中只需要配置相应类型的组件,容器就可以自动找到。如果找不到就调用getDefaultStrategy按照类型获取默认的组件。需要注意的是,这里的context指的是Frame-workServlet中创建的WebApplicationContext,而不是ServletContext。下面介绍getDefault-Strategy是怎样获取默认组件的。
// org.springframework.web.servlet.DispatcherServletprotectedT getDefaultStrategy(ApplicationContext context, Class strategyInterface) { List strategies = getDefaultStrategies(context, strategyInterface); if (strategies.size() != 1) { throw new BeanInitializationException( "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]"); } return strategies.get(0);}protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); //从defaultStrategies获取所需策略的类型 String value = defaultStrategies.getProperty(key); if (value != null) { //如果有多个默认值,以逗号分割为数组 String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List 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( "Error loading DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]: problem with class file or dependent class", err); } } return strategies; }else { return new LinkedList (); }}
可以看到getDefaultStrategy中调用了getDefaultStrategies,后者返回的是List,这是因为HandlerMapping等组件可以有多个,所以定义了getDefaultStrategies方法,getDefaultStrategy直接调用了getDefaultStrategies方法,并返回返回值的第一个结果。getDefaultStrategies中实际执行创建的方法是ClassUtils.forName,它需要的参数是class-Name,所以最重要的是看className怎么来的,找到了className的来源,也就可以理解默认初始化的方式。className来自classNames,classNames又来自value,而value来自default-Strategies.getProperty(key)。所以关键点就在defaultStrategies中,defaultStrategies是一个静态属性,在static块中进行初始化的。
// org.springframework.web.servlet.DispatcherServletprivate static final Properties defaultStrategies;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()); }}
# org.springframework.web.DispatcherServlet.properties# 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.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
我们看到defaultStrategies是DispatcherServlet类所在包下的DEFAULT_STRATEGIES_PATH文件里定义的属性,DEFAULT_STRATEGIES_PATH的值是DispatcherServlet.properties。所以defaultStrategies里面存放的是org.springframework.web.DispatcherServlet.properties里面定义的键值对,代码如上。
可以看到,这里确实定义了不同组件的类型,一共定义了8个组件,处理上传组件Multi-partResolver是没有默认配置的,这也很容易理解,并不是每个应用都需要上传功能,即使需要上传也不一定就要使用MultipartResolver,所以MultipartResolver不需要默认配置。另外HandlerMapping、HandlerAdapter和HandlerExceptionResolver都配置了多个,其实View-Resolver也可以有多个,只是默认的配置只有一个。
这里需要注意两个问题:首先默认配置并不是最优配置,也不是spring的推荐配置,只是在没有配置的时候可以有个默认值,不至于空着。里面的有些默认配置甚至已经被标注为@Deprecated,表示已弃用,如DefaultAnnotationHandlerMapping、Annotation-MethodHandlerSolver不需要默认配置。另外HandlerMapping、andlerAdapter和HandlerExceptionResolver都配置了多个,其实View-Resolver也可以有多个,只是默认的配置只有一个。
DispatcherServlet的创建过程主要是对9大组件进行初始化,具体每个组件的作用后面具体讲解。