在上一篇文章中,我們從DispatcherServlet談起,最終為讀者詳細分析了SpringMVC的初始化主線的全部過程。整個初始化主線的研究,其實始終圍繞着DispatcherServlet、WebApplicationContext和組件這三大元素之間的關系展開。
在文章寫完之后,也陸續收到了一些反饋,其中比較集中的問題,是有關WebApplicationContext對組件進行初始化的過程交代的不夠清楚。所以,本文作為上一篇文章的續文,就試圖來講清楚這個話題。
SpringMVC的核心配置文件
SpringMVC的核心配置文件,我們從整個專欄的第一篇文章就開始接觸。所以,我們在這里首先對SpringMVC的核心配置文件做一些概括性的回顧。
這是我們在講有關SpringMVC的構成要素時就曾經提到過的一個重要結論。當時我們所說的另外兩大必要元素就是DispatcherServlet和Controller。因而,SpringMVC的核心配置文件在整個應用程序中所起到的作用也是舉足輕重的。這也就是我們在這里需要補充對這個文件進行詳細分析的原因。
這個結論很容易理解。作為Spring Framework的一部分,我們可以認為SpringMVC是整個Spring Framework的一個組件。因而兩者的配置體系和管理體系完全相同也屬情理之中。實際上,SpringMVC所采取的策略,就是借用Spring Framework強大的容器(ApplicationContext)功能,而絕非自行實現。
我們在web.xml中指定SpringMVC的入口程序DispatcherServlet時,實際上蘊含了一個對核心配置文件的指定過程([servlet-name]-servlet.xml)。當然,我們也可以明確指定這個核心配置文件的位置。這些配置選項,我們已經在上一篇文章中詳細介紹過,這里不再重復。
而上面這一結論,除了說明兩者之間的配置關系之外,還包含了一層運行關系:DispatcherServlet負責對WebApplicationContext進行初始化,而初始化的依據,就是這個SpringMVC的核心配置文件。所以,SpringMVC的核心配置文件的內容解讀將揭開整個SpringMVC初始化主線的全部秘密。
如果我們把這個結論與上一個結論結合起來來看,也正因為SpringMVC的核心配置文件使用了與Spring Framework相同的格式,才使其成為DispatcherServlet駕馭Spring的窗口。
這個結論告訴了我們SpringMVC核心配置文件在整個框架中的作用。組件行為模式的多樣化,決定了我們必須借助一個容器(WebApplicationContext)來進行統一的管理。而SpringMVC的核心配置文件,就是我們進行組件管理的窗口。
核心配置文件概覽
說了那么多有關SpringMVC核心配置文件的結論,我們不妨來看一下這個配置文件的概況:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.1.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
- <!-- Enables the Spring MVC @Controller programming model -->
- <mvc:annotation-driven />
- <context:component-scan base-package="com.demo2do.sample.web.controller" />
- <!-- Handles HTTP GET requests for /static/** by efficiently serving up static resources in the ${webappRoot}/static/ directory -->
- <mvc:resources mapping="/static/**" location="/static/" />
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/" />
- <property name="suffix" value=".jsp" />
- </bean>
- </beans>
這是一個非常典型的SpringMVC核心配置文件。雖然我們在這里幾乎對每一段重要的配置都做了注釋,不過可能對於毫無SpringMVC開發經驗的讀者來說,這段配置基本上還無法閱讀。所以接下來,我們就試圖對這個文件中的一些細節加以說明。
【頭部聲明】
配置文件中首先進入我們眼簾的是它的頭部的一大段聲明:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.1.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
- ......
- </beans>
這個部分是整個SpringMVC核心配置文件的關鍵所在。這一段聲明,被稱之為Schema-based XML的聲明部分。有關Schema-based XML的概念,讀者可以參考Spring官方的reference:
Appendix C. XML Schema-based configuration
Appendix D. Extensible XML authoring
為了幫助讀者快速理解,我們稍后會專門開辟章節針對Schema-based XML的來龍去脈進行講解。
【組件定義】
除了頭部聲明部分的其他配置部分,就是真正的組件定義部分。在這個部分中,我們可以看到兩種不同類型的配置定義模式:
1. 基於Schema-based XML的配置定義模式
- <mvc:annotation-driven />
2. 基於Traditional XML的配置定義模式
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/" />
- <property name="suffix" value=".jsp" />
- lt;/bean>
兩種不同的組件定義模式,其目的是統一的:對SpringMVC中的組件進行聲明,指定組件的行為方式。
雖然兩種不同的組件定義模式的外在表現看上去有所不同,但是SpringMVC在對其進行解析時最終都會將其轉化為組件的定義而加載到WebApplicationContext之中進行管理。所以我們需要理解的是蘊藏在配置背后的目的而非配置本身的形式。
至於這兩種不同的配置形式之間的關系,我們稍后會在Schema-based XML的講解中詳細展開。
Schema-based XML
【基本概念】
Schema-based XML本身並不是SpringMVC或者Spring Framework獨創的一種配置模式。我們可以看看W3C對於其用途的一個大概解釋:
這個解釋稍微有點抽象。所以我們可以來看看Spring官方reference對於引入Schema-based XML的說法:
也就是說,我們引入Schema-based XML是為了對Traditional的XML配置形式進行簡化。通過Schema的定義,把一些原本需要通過幾個bean的定義或者復雜的bean的組合定義的配置形式,用另外一種簡單而可讀的配置形式呈現出來。
所以,我們也可以由此得出一些有用的推論:
這里的代替一詞非常重要,這就意味着傳統的XML配置形式在這里會被顛覆,我們在對Schema-based XML進行解讀時,需要使用一種全新的語義規范來理解。
這是從引入Schema-based XML的目的反過來得出的推論。因為如果引入Schema-based XML之后,整個配置變得更加復雜,那么Schema-based XML的引入也就失去了意義。
同時,筆者在這里需要特別強調的是Schema-based XML的引入,實際上是把原本靜態的配置動態化、過程化。有關這一點,我們稍后會有說明。
【引入目的】
在早期的Spring版本中,只有Traditional XML一種組件定義模式。當時,XML作為Java最好的朋友,自然而然在整個框架中起到了舉足輕重的作用。根據Spring的設計原則,所有納入WebApplicationContext中管理的對象,都被映射為XML中的一個<bean>節點,通過對於<bean>節點的一個完整描述,我們可以有效地將整個應用程序中所有的對象都納入到一個統一的容器中進行管理。
這種統一化的描述,帶來的是管理上的便利,不過同時也帶來了邏輯上的困擾。因為統一的節點,降低了配置的難度,我們幾乎只需要將<bean>節點與Java的對象模型對應起來即可。(有一定經驗的Spring程序員可以回憶一下,我們在編寫Spring配置文件時,是否也是一個將配置選項與Java對象中屬性或者方法對應起來的過程)但是這樣的配置形式本身並不具備邏輯語義,也就是說我們無法非常直觀地看出某一個特定的<bean>定義,從邏輯上它到底想說明什么問題?
這也就是后來Schema-based XML開始被引入並流行開來的重要原因。從形式上看,Schema-based XML相比較Traditional XML至少有三個方面的優勢:
- namespace —— 擁有很明確的邏輯分類
- element —— 擁有很明確的過程語義
- attributes —— 擁有很簡明的配置選項
這三方面的優勢,我們可以用一幅圖來進行說明:
在圖中,我們分別用上下兩層來說明某一個配置節點的結構名稱以及它們的具體作用。由此可見,Schema-based XML中的配置節點擁有比較鮮明的功能特性,通過namespace、element和attributes這三大元素之間的配合,共同完成對一個動態過程的描述。
例如,<mvc:annotation-driven />這段配置想要表達的意思,就是在mvc的空間內實現Annotation驅動的配置方式。其中,mvc表示配置的有效范圍,annotation-driven則表達了一個動態的過程,實際的邏輯含義是:整個SpringMVC的實現是基於Annotation模式,請為我注冊相關的行為模式。
這種配置方式下,可讀性大大提高:我們無需再去理解其中的實現細節。同時,配置的簡易性也大大提高:我們甚至不用去關心哪些bean被定義了。
所以總體來說,Schema-based XML的引入,對於配置的簡化是一個極大的進步。
【構成要素】
在Spring中,一個Schema-based XML有兩大構成要素:過程實現和配置定義。
先談談過程實現。所謂過程實現,其實就是我們剛才所舉的那個例子中,實現實際背后邏輯的過程。這個過程由兩個Java接口來進行表述:
- NamespaceHandler —— 對Schema定義中namespace的邏輯處理接口
- BeanDefinitionParser —— 對Schema定義中element的邏輯處理接口
很顯然,NamespaceHandler是入口程序,它包含了所有的屬於該namespace定義下所有element的處理調用,所以BeanDefinitionParser的實現就成為了NamespaceHandler的調用對象了。這一點,我們可以通過NamesapceHandler的MVC實現類來加以證明:
- public void init() {
- registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
- registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
- registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
- registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
- registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
- }
我們可以看到,MvcNamespaceHandler的執行,只不過依次調用了不同的BeanDefinitionParser的實現類而已,而每一個BeanDefinitionParser的實現,則對應於Schema定義中的element邏輯處理。例如,AnnotationDrivenBeanDefinitionParser對應於:<mvc:annotation-driven />這個element實現;ResourcesBeanDefinitionParser則對應於<mvc:resources />的實現等等。
所以,要具體了解每個element的行為過程,只要研究每一個BeanDefinitionParser的實現類即可。我們以整個MVC空間中最重要的一個節點<mvc:annotation-driven />為例,對AnnotationDrivenBeanDefinitionParser進行說明,其源碼如下:
- public BeanDefinition parse(Element element, ParserContext parserContext) {
- Object source = parserContext.extractSource(element);
- CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
- parserContext.pushContainingComponent(compDefinition);
- RootBeanDefinition methodMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
- methodMappingDef.setSource(source);
- methodMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- methodMappingDef.getPropertyValues().add("order", 0);
- String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(methodMappingDef);
- RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
- RuntimeBeanReference validator = getValidator(element, source, parserContext);
- RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element, source, parserContext);
- RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
- bindingDef.setSource(source);
- bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- bindingDef.getPropertyValues().add("conversionService", conversionService);
- bindingDef.getPropertyValues().add("validator", validator);
- bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
- ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
- ManagedList<?> argumentResolvers = getArgumentResolvers(element, source, parserContext);
- ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, source, parserContext);
- RootBeanDefinition methodAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
- methodAdapterDef.setSource(source);
- methodAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- methodAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
- methodAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
- if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
- Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
- methodAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
- }
- if (argumentResolvers != null) {
- methodAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
- }
- if (returnValueHandlers != null) {
- methodAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
- }
- String methodAdapterName = parserContext.getReaderContext().registerWithGeneratedName(methodAdapterDef);
- RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
- csInterceptorDef.setSource(source);
- csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
- RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
- mappedCsInterceptorDef.setSource(source);
- mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
- mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
- String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);
- RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
- methodExceptionResolver.setSource(source);
- methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
- methodExceptionResolver.getPropertyValues().add("order", 0);
- String methodExceptionResolverName =
- parserContext.getReaderContext().registerWithGeneratedName(methodExceptionResolver);
- RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
- responseStatusExceptionResolver.setSource(source);
- responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- responseStatusExceptionResolver.getPropertyValues().add("order", 1);
- String responseStatusExceptionResolverName =
- parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);
- RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
- defaultExceptionResolver.setSource(source);
- defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- defaultExceptionResolver.getPropertyValues().add("order", 2);
- String defaultExceptionResolverName =
- parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);
- parserContext.registerComponent(new BeanComponentDefinition(methodMappingDef, methodMappingName));
- parserContext.registerComponent(new BeanComponentDefinition(methodAdapterDef, methodAdapterName));
- parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExceptionResolverName));
- parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
- parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
- parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
- // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
- MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
- parserContext.popAndRegisterContainingComponent();
- return null;
- }
整個過程看上去稍顯凌亂,不過我們發現其中圍繞的一條主線就是:使用編程的方式來對bean進行注冊。也就是說,<mvc:annotation-driven />這樣一句配置,頂上了我們如此多的bean定義。難怪Schema-based XML被譽為是簡化XML配置的絕佳幫手了。
有了過程實現,我們再來談談配置定義。配置定義的目的非常簡單,就是通過一些配置文件,將上述的過程實現類串聯起來,從而完成整個Schema-based XML的定義。
整個配置定義,也分為兩個部分:
- Schema定義 —— 一個xsd文件,描述整個Schema空間中element和attribute的定義
- 注冊配置文件 —— 由META-INF/spring.handlers和META-INF/spring.schemas構成,用以注冊Schema和Handler
Schema定義是由一個xsd文件完成的。這個文件在Spring發布的時候同時發布在網絡上。例如SpringMVC的Schema定義,就發布在這個地址:
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
同時,這個地址在Spring的發布包中也存有一個備份。這個備份位於SpringMVC的分發包spring-webmvc的JAR包之中。
這樣做的好處在於,我們在對Schema進行引用時,可以通過本地尋址來加快加載速度。
注:如果我們回顧一下之前的核心配置文件中的頭部聲明部分。其中的xsi:schemaLocation聲明就是用於指定映射於本地的XSD文件。所以xsi:schemaLocation的定義不是必須的,不過聲明它能夠使Spring自動查找本地的緩存來進行schema的尋址。
我們在這里不對XSD文件做過多的內容分析,因為其中不外乎是對element的定義、attributes的定義等等。這些內容是我們進行Schema-based XML配置的核心基礎。
配置定義的另外一個元素構成是META-INF/spring.handlers和META-INF/spring.schemas這兩個文件。它們同樣位於SpringMVC的分發包下。當我們在XML的頭部聲明中引用了相關的Schema定義之后,Spring會自動查找spring.schemas和spring.handlers的定義,根據其中指定的NamespaceHandler實現類加載執行。
有關這個過程,我們在之后的日志分析中還會涉及。
初始化日志的再分析
有了Schema Based XML的相關知識,就可以對DispatcherServlet的初始化啟動日志做進一步的詳細分析。而這次的分析,我們試圖弄清楚以下問題:
- Where —— 組件的聲明在哪里?
- How —— 組件是如何被注冊的?
- What —— 究竟哪些組件被注冊了?
對於這三個問題的研究,我們需要結合日志和Schema based XML的運行機理來共同進行分析。
19:49:48,670 INFO XmlWebApplicationContext:495 - Refreshing WebApplicationContext for namespace 'dispatcher-servlet': startup date [Thu Feb 16 19:49:48 CST 2012]; parent: Root WebApplicationContext
19:49:48,674 INFO XmlBeanDefinitionReader:315 - Loading XML bean definitions from class path resource [web/applicationContext-dispatcher.xml]
## Schema定位和加載 (開始) ##
19:49:48,676 DEBUG DefaultDocumentLoader:72 - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
19:49:48,678 DEBUG PluggableSchemaResolver:140 - Loading schema mappings from [META-INF/spring.schemas]
19:49:48,690 DEBUG PluggableSchemaResolver:118 - Found XML schema [http://www.springframework.org/schema/beans/spring-beans-3.1.xsd] in classpath: org/springframework/beans/factory/xml/spring-beans-3.1.xsd
19:49:48,710 DEBUG PluggableSchemaResolver:118 - Found XML schema [http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd] in classpath: org/springframework/web/servlet/config/spring-mvc-3.1.xsd
19:49:48,715 DEBUG PluggableSchemaResolver:118 - Found XML schema [http://www.springframework.org/schema/tool/spring-tool-3.1.xsd] in classpath: org/springframework/beans/factory/xml/spring-tool-3.1.xsd
19:49:48,722 DEBUG PluggableSchemaResolver:118 - Found XML schema [http://www.springframework.org/schema/context/spring-context-3.1.xsd] in classpath: org/springframework/context/config/spring-context-3.1.xsd
## Schema定位和加載 (結束) ##
## NamespaceHandler執行階段 (開始) ##
19:49:48,731 DEBUG DefaultBeanDefinitionDocumentReader:108 - Loading bean definitions
19:49:48,742 DEBUG DefaultNamespaceHandlerResolver:156 - Loaded NamespaceHandler mappings: {...}
19:49:48,886 DEBUG PathMatchingResourcePatternResolver:550 - Looking for matching resources in directory tree [D:\Work\Demo2do\Sample\target\classes\com\demo2do\sample\web\controller]
19:49:48,896 DEBUG XmlBeanDefinitionReader:216 - Loaded 18 bean definitions from location pattern [classpath:web/applicationContext-dispatcher.xml]
19:49:48,897 DEBUG XmlWebApplicationContext:525 - Bean factory for WebApplicationContext for namespace 'dispatcher-servlet': org.springframework.beans.factory.support.DefaultListableBeanFactory@495c998a: defining beans [[
1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0,
2. org.springframework.format.support.FormattingConversionServiceFactoryBean#0,
3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0,
4. org.springframework.web.servlet.handler.MappedInterceptor#0,
5. org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0,
6. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0,
7. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0,
8. org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
9. org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
10. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
11. blogController,
12. userController,
13. org.springframework.context.annotation.internalConfigurationAnnotationProcessor,
14. org.springframework.context.annotation.internalAutowiredAnnotationProcessor,
15. org.springframework.context.annotation.internalRequiredAnnotationProcessor,
16. org.springframework.context.annotation.internalCommonAnnotationProcessor,
17. org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0,
18. org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#0
]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@6602e323
19:49:48,949 DEBUG XmlWebApplicationContext:794 - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@4b2922f6]
19:49:48,949 DEBUG XmlWebApplicationContext:818 - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@79b66b06]
19:49:48,949 DEBUG UiApplicationContextUtils:85 - Unable to locate ThemeSource with name 'themeSource': using default [org.springframework.ui.context.support.DelegatingThemeSource@372c9557]
19:49:49,154 DEBUG RequestMappingHandlerMapping:98 - Looking for request mappings in application context: WebApplicationContext for namespace 'dispatcher-servlet': startup date [Thu Feb 16 19:49:48 CST 2012]; parent: Root WebApplicationContext
19:49:49,175 INFO RequestMappingHandlerMapping:188 - Mapped "{[/blog],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto
public org.springframework.web.servlet.ModelAndView com.demo2do.sample.web.controller.BlogController.index()
19:49:49,177 INFO RequestMappingHandlerMapping:188 - Mapped "{[/register],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto
public org.springframework.web.servlet.ModelAndView com.demo2do.sample.web.controller.UserController.register(com.demo2do.sample.entity.User)
19:49:49,180 INFO RequestMappingHandlerMapping:188 - Mapped "{[/login],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto
public org.springframework.web.servlet.ModelAndView com.demo2do.sample.web.controller.UserController.login(java.lang.String,java.lang.String)
19:49:49,632 DEBUG BeanNameUrlHandlerMapping:71 - Looking for URL mappings in application context: WebApplicationContext for namespace 'dispatcher-servlet': startup date [Thu Feb 16 19:49:48 CST 2012]; parent: Root WebApplicationContext
19:49:49,924 INFO SimpleUrlHandlerMapping:314 - Mapped URL path [/static/**] onto handler 'org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0'
## NamespaceHandler執行階段 (結束) ##
19:49:49,956 DEBUG DispatcherServlet:627 - Unable to locate RequestToViewNameTranslator with name 'viewNameTranslator': using default [org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@4d16318b]
19:49:49,980 DEBUG DispatcherServlet:667 - No ViewResolvers found in servlet 'dispatcher': using default
19:49:49,986 DEBUG DispatcherServlet:689 - Unable to locate FlashMapManager with name 'flashMapManager': using default [org.springframework.web.servlet.support.DefaultFlashMapManager@1816daa9]
19:49:49,986 DEBUG DispatcherServlet:523 - Published WebApplicationContext of servlet 'dispatcher' as ServletContext attribute with name [org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher]
19:49:49,986 INFO DispatcherServlet:463 - FrameworkServlet 'dispatcher': initialization completed in 1320 ms
19:49:49,987 DEBUG DispatcherServlet:136 - Servlet 'dispatcher' configured successfully
在上面的啟動日志中,筆者還是把不同的日志功能使用不同的顏色進行了區分。這里進行逐一分析:
1. 黑色加粗標記區域 —— 容器的啟動和結束標志
這個部分的日志比較明顯,位於容器的啟動階段和結束階段,在之前的討論中我們已經分析過,這里不再重復。
2. 黃色注釋段落 —— Schema定位和加載
這個部分的日志反應出剛才我們所分析的Schema-based XML的工作原理。這是其中的第一步:讀取META-INF/spring.schemas的內容,加載schema定義。然后找到相應的NamespaceHandler,執行其實現類。
3. 藍色注釋部分 —— NamespaceHandler執行階段
這個部分的日志,可以幫助我們回答本節一開始所提出的兩個問題。絕大多數的組件,都是在BeanDefinitionParser的實現類中使用編程的方式注冊的。
4. 紅色標記區域 —— 組件注冊細節
這個部分的日志區域徹底回答了本節一開始所提出的最后一個問題:一共有18個組件被注冊,就是紅色標記的那18個bean。
小結
本文所涉及到的話題,主要圍繞着SpringMVC的核心配置問題展開。讀者可以將本文作為上一篇文章的續篇,將兩者結合起來閱讀。因為從宏觀上說,本文的話題實際上也屬於初始化主線的一個部分。
轉自:http://downpour.iteye.com/blog/1389285