SpringMVC源碼 3.2 DispatchServlet 組件初始化

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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章