當(dāng)前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
SpringMVC深度探险 —— SpringMVC核心配置文件详解
生活随笔
收集整理的這篇文章主要介紹了
SpringMVC深度探险 —— SpringMVC核心配置文件详解
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在上一篇文章中,我們從DispatcherServlet談起,最終為讀者詳細(xì)分析了SpringMVC的初始化主線的全部過程。整個(gè)初始化主線的研究,其實(shí)始終圍繞著DispatcherServlet、WebApplicationContext和組件這三大元素之間的關(guān)系展開。?
在文章寫完之后,也陸續(xù)收到了一些反饋,其中比較集中的問題,是有關(guān)WebApplicationContext對(duì)組件進(jìn)行初始化的過程交代的不夠清楚。所以,本文作為上一篇文章的續(xù)文,就試圖來講清楚這個(gè)話題。?
SpringMVC的核心配置文件 ?
SpringMVC的核心配置文件,我們從整個(gè)專欄的第一篇文章就開始接觸。所以,我們?cè)谶@里首先對(duì)SpringMVC的核心配置文件做一些概括性的回顧。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件是構(gòu)成SpringMVC應(yīng)用程序的必要元素之一。
這是我們?cè)谥v有關(guān)SpringMVC的構(gòu)成要素時(shí)就曾經(jīng)提到過的一個(gè)重要結(jié)論。當(dāng)時(shí)我們所說的另外兩大必要元素就是DispatcherServlet和Controller。因而,SpringMVC的核心配置文件在整個(gè)應(yīng)用程序中所起到的作用也是舉足輕重的。這也就是我們?cè)谶@里需要補(bǔ)充對(duì)這個(gè)文件進(jìn)行詳細(xì)分析的原因。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件與傳統(tǒng)的Spring Framework的配置文件是一脈相承的。
這個(gè)結(jié)論很容易理解。作為Spring Framework的一部分,我們可以認(rèn)為SpringMVC是整個(gè)Spring Framework的一個(gè)組件。因而兩者的配置體系和管理體系完全相同也屬情理之中。實(shí)際上,SpringMVC所采取的策略,就是借用Spring Framework強(qiáng)大的容器(ApplicationContext)功能,而絕非自行實(shí)現(xiàn)。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件是架起DispatcherServlet與WebApplicationContext之間的橋梁。
我們?cè)趙eb.xml中指定SpringMVC的入口程序DispatcherServlet時(shí),實(shí)際上蘊(yùn)含了一個(gè)對(duì)核心配置文件的指定過程([servlet-name]-servlet.xml)。當(dāng)然,我們也可以明確指定這個(gè)核心配置文件的位置。這些配置選項(xiàng),我們已經(jīng)在上一篇文章中詳細(xì)介紹過,這里不再重復(fù)。?
而上面這一結(jié)論,除了說明兩者之間的配置關(guān)系之外,還包含了一層運(yùn)行關(guān)系: DispatcherServlet負(fù)責(zé)對(duì)WebApplicationContext進(jìn)行初始化,而初始化的依據(jù),就是這個(gè)SpringMVC的核心配置文件。 所以,SpringMVC的核心配置文件的內(nèi)容解讀將揭開整個(gè)SpringMVC初始化主線的全部秘密。?
如果我們把這個(gè)結(jié)論與上一個(gè)結(jié)論結(jié)合起來來看,也正因?yàn)镾pringMVC的核心配置文件使用了與Spring Framework相同的格式,才使其成為DispatcherServlet駕馭Spring的窗口。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件是SpringMVC中所有組件的定義窗口,通過它我們可以指定整個(gè)SpringMVC的行為方式。
這個(gè)結(jié)論告訴了我們SpringMVC核心配置文件在整個(gè)框架中的作用。組件行為模式的多樣化,決定了我們必須借助一個(gè)容器(WebApplicationContext)來進(jìn)行統(tǒng)一的管理。而SpringMVC的核心配置文件,就是我們進(jìn)行組件管理的窗口。?
核心配置文件概覽 ?
說了那么多有關(guān)SpringMVC核心配置文件的結(jié)論,我們不妨來看一下這個(gè)配置文件的概況:?
Xml代碼?? <?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>??
這是一個(gè)非常典型的SpringMVC核心配置文件。雖然我們?cè)谶@里幾乎對(duì)每一段重要的配置都做了注釋,不過可能對(duì)于毫無SpringMVC開發(fā)經(jīng)驗(yàn)的讀者來說,這段配置基本上還無法閱讀。所以接下來,我們就試圖對(duì)這個(gè)文件中的一些細(xì)節(jié)加以說明。?
【頭部聲明】 ?
配置文件中首先進(jìn)入我們眼簾的是它的頭部的一大段聲明:?
Xml代碼?? <?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>??
這個(gè)部分是整個(gè)SpringMVC核心配置文件的關(guān)鍵所在。這一段聲明,被稱之為 Schema-based XML的聲明 部分。有關(guān)Schema-based XML的概念,讀者可以參考Spring官方的reference:?
Appendix C. XML Schema-based configuration ?
Appendix D. Extensible XML authoring ?
為了幫助讀者快速理解,我們稍后會(huì)專門開辟章節(jié)針對(duì)Schema-based XML的來龍去脈進(jìn)行講解。?
【組件定義】 ?
除了頭部聲明部分的其他配置部分,就是真正的 組件定義 部分。在這個(gè)部分中,我們可以看到兩種不同類型的配置定義模式:?
1. 基于Schema-based XML的配置定義模式 ?
Xml代碼?? <mvc:annotation-driven?/>??
2. 基于Traditional XML的配置定義模式 ?
Xml代碼?? <bean?class="org.springframework.web.servlet.view.InternalResourceViewResolver">???? ???????<property?name="prefix"?value="/"?/>???? ???????<property?name="suffix"?value=".jsp"?/>???? lt;/bean>??
兩種不同的組件定義模式,其目的是統(tǒng)一的: 對(duì)SpringMVC中的組件進(jìn)行聲明,指定組件的行為方式。 ?
雖然兩種不同的組件定義模式的外在表現(xiàn)看上去有所不同,但是SpringMVC在對(duì)其進(jìn)行解析時(shí)最終都會(huì)將其轉(zhuǎn)化為組件的定義而加載到WebApplicationContext之中進(jìn)行管理。所以我們需要理解的是蘊(yùn)藏在配置背后的目的而非配置本身的形式。?
至于這兩種不同的配置形式之間的關(guān)系,我們稍后會(huì)在Schema-based XML的講解中詳細(xì)展開。?
Schema-based XML ?
【基本概念】 ?
Schema-based XML本身并不是SpringMVC或者Spring Framework獨(dú)創(chuàng)的一種配置模式。我們可以看看W3C對(duì)于其用途的一個(gè)大概解釋:?
W3C 寫道 The purpose of an XSD schema is to define and describe a class of XML documents by using schema components to constrain and document the meaning, usage and relationships of their constituent parts: datatypes, elements and their content and attributes and their values. Schemas can also provide for the specification of additional document information, such as normalization and defaulting of attribute and element values. Schemas have facilities for self-documentation. Thus, XML Schema Definition Language: Structures can be used to define, describe and catalogue XML vocabularies for classes of XML documents.
這個(gè)解釋稍微有點(diǎn)抽象。所以我們可以來看看Spring官方reference對(duì)于引入Schema-based XML的說法:?
Spring Reference 寫道 The central motivation for moving to XML Schema based configuration files was to make Spring XML configuration easier. The 'classic' <bean/>-based approach is good, but its generic-nature comes with a price in terms of configuration overhead.
也就是說,我們引入Schema-based XML是為了對(duì)Traditional的XML配置形式進(jìn)行簡(jiǎn)化。 通過Schema的定義,把一些原本需要通過幾個(gè)bean的定義或者復(fù)雜的bean的組合定義的配置形式,用另外一種簡(jiǎn)單而可讀的配置形式呈現(xiàn)出來。 ?
所以,我們也可以由此得出一些有用的推論:?
downpour 寫道 Schema-based XML可以代替Traditional的XML配置形式,在Spring容器中進(jìn)行組件的定義。
這里的代替一詞非常重要,這就意味著傳統(tǒng)的XML配置形式在這里會(huì)被顛覆,我們?cè)趯?duì)Schema-based XML進(jìn)行解讀時(shí),需要使用一種全新的語義規(guī)范來理解。?
downpour 寫道 Schema-based XML可以極大改善配置文件的可讀性并且縮小配置文件的規(guī)模。
這是從引入Schema-based XML的目的反過來得出的推論。因?yàn)槿绻隨chema-based XML之后,整個(gè)配置變得更加復(fù)雜,那么Schema-based XML的引入也就失去了意義。?
同時(shí),筆者在這里需要特別強(qiáng)調(diào)的是Schema-based XML的引入,實(shí)際上是 把原本靜態(tài)的配置動(dòng)態(tài)化、過程化。 有關(guān)這一點(diǎn),我們稍后會(huì)有說明。?
【引入目的】 ?
在早期的Spring版本中,只有Traditional XML一種組件定義模式。當(dāng)時(shí),XML作為Java最好的朋友,自然而然在整個(gè)框架中起到了舉足輕重的作用。根據(jù)Spring的設(shè)計(jì)原則,所有納入WebApplicationContext中管理的對(duì)象,都被映射為XML中的一個(gè)<bean>節(jié)點(diǎn),通過對(duì)于<bean>節(jié)點(diǎn)的一個(gè)完整描述,我們可以有效地將整個(gè)應(yīng)用程序中所有的對(duì)象都納入到一個(gè)統(tǒng)一的容器中進(jìn)行管理。?
這種統(tǒng)一化的描述,帶來的是管理上的便利,不過同時(shí)也帶來了邏輯上的困擾。因?yàn)榻y(tǒng)一的節(jié)點(diǎn),降低了配置的難度,我們幾乎只需要將<bean>節(jié)點(diǎn)與Java的對(duì)象模型對(duì)應(yīng)起來即可。(有一定經(jīng)驗(yàn)的Spring程序員可以回憶一下,我們?cè)诰帉慡pring配置文件時(shí),是否也是一個(gè)將配置選項(xiàng)與Java對(duì)象中屬性或者方法對(duì)應(yīng)起來的過程)但是這樣的配置形式本身并不具備邏輯語義,也就是說我們無法非常直觀地看出某一個(gè)特定的<bean>定義,從邏輯上它到底想說明什么問題??
這也就是后來Schema-based XML開始被引入并流行開來的重要原因。從形式上看,Schema-based XML相比較Traditional XML至少有三個(gè)方面的優(yōu)勢(shì):?
?
在圖中,我們分別用上下兩層來說明某一個(gè)配置節(jié)點(diǎn)的結(jié)構(gòu)名稱以及它們的具體作用。由此可見, Schema-based XML中的配置節(jié)點(diǎn)擁有比較鮮明的功能特性,通過namespace、element和attributes這三大元素之間的配合,共同完成對(duì)一個(gè)動(dòng)態(tài)過程的描述。 ?
例如,<mvc:annotation-driven />這段配置想要表達(dá)的意思,就是在mvc的空間內(nèi)實(shí)現(xiàn)Annotation驅(qū)動(dòng)的配置方式。其中,mvc表示配置的有效范圍,annotation-driven則表達(dá)了一個(gè)動(dòng)態(tài)的過程,實(shí)際的邏輯含義是:整個(gè)SpringMVC的實(shí)現(xiàn)是基于Annotation模式,請(qǐng)為我注冊(cè)相關(guān)的行為模式。?
這種配置方式下,可讀性大大提高:我們無需再去理解其中的實(shí)現(xiàn)細(xì)節(jié)。同時(shí),配置的簡(jiǎn)易性也大大提高:我們甚至不用去關(guān)心哪些bean被定義了。?
所以總體來說,Schema-based XML的引入,對(duì)于配置的簡(jiǎn)化是一個(gè)極大的進(jìn)步。?
【構(gòu)成要素】 ?
在Spring中,一個(gè)Schema-based XML有兩大構(gòu)成要素: 過程實(shí)現(xiàn) 和 配置定義 。?
先談?wù)勥^程實(shí)現(xiàn)。所謂過程實(shí)現(xiàn),其實(shí)就是我們剛才所舉的那個(gè)例子中,實(shí)現(xiàn)實(shí)際背后邏輯的過程。這個(gè)過程由兩個(gè)Java接口來進(jìn)行表述:?
Java代碼?? 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的執(zhí)行,只不過依次調(diào)用了不同的BeanDefinitionParser的實(shí)現(xiàn)類而已,而每一個(gè)BeanDefinitionParser的實(shí)現(xiàn),則對(duì)應(yīng)于Schema定義中的element邏輯處理。例如,AnnotationDrivenBeanDefinitionParser對(duì)應(yīng)于:<mvc:annotation-driven />這個(gè)element實(shí)現(xiàn);ResourcesBeanDefinitionParser則對(duì)應(yīng)于<mvc:resources />的實(shí)現(xiàn)等等。?
所以,要具體了解每個(gè)element的行為過程,只要研究每一個(gè)BeanDefinitionParser的實(shí)現(xiàn)類即可。我們以整個(gè)MVC空間中最重要的一個(gè)節(jié)點(diǎn)<mvc:annotation-driven />為例,對(duì)AnnotationDrivenBeanDefinitionParser進(jìn)行說明,其源碼如下:?
Java代碼?? 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;?? }??
整個(gè)過程看上去稍顯凌亂,不過我們發(fā)現(xiàn)其中圍繞的一條主線就是: 使用編程的方式來對(duì)bean進(jìn)行注冊(cè)。 也就是說,<mvc:annotation-driven />這樣一句配置,頂上了我們?nèi)绱硕嗟腷ean定義。難怪Schema-based XML被譽(yù)為是簡(jiǎn)化XML配置的絕佳幫手了。?
有了過程實(shí)現(xiàn),我們?cè)賮碚務(wù)勁渲枚x。配置定義的目的非常簡(jiǎn)單,就是通過一些配置文件,將上述的過程實(shí)現(xiàn)類串聯(lián)起來,從而完成整個(gè)Schema-based XML的定義。?
整個(gè)配置定義,也分為兩個(gè)部分:?
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd ?
同時(shí),這個(gè)地址在Spring的發(fā)布包中也存有一個(gè)備份。這個(gè)備份位于SpringMVC的分發(fā)包spring-webmvc的JAR包之中。?
?
這樣做的好處在于,我們?cè)趯?duì)Schema進(jìn)行引用時(shí),可以通過本地尋址來加快加載速度。?
注:如果我們回顧一下之前的核心配置文件中的頭部聲明部分。其中的xsi:schemaLocation聲明就是用于指定映射于本地的XSD文件。所以xsi:schemaLocation的定義不是必須的,不過聲明它能夠使Spring自動(dòng)查找本地的緩存來進(jìn)行schema的尋址。 ?
我們?cè)谶@里不對(duì)XSD文件做過多的內(nèi)容分析,因?yàn)槠渲胁煌夂跏菍?duì)element的定義、attributes的定義等等。這些內(nèi)容是我們進(jìn)行Schema-based XML配置的核心基礎(chǔ)。?
配置定義的另外一個(gè)元素構(gòu)成是META-INF/spring.handlers和META-INF/spring.schemas這兩個(gè)文件。它們同樣位于SpringMVC的分發(fā)包下。當(dāng)我們?cè)赬ML的頭部聲明中引用了相關(guān)的Schema定義之后,Spring會(huì)自動(dòng)查找spring.schemas和spring.handlers的定義,根據(jù)其中指定的NamespaceHandler實(shí)現(xiàn)類加載執(zhí)行。?
有關(guān)這個(gè)過程,我們?cè)谥蟮娜罩痉治鲋羞€會(huì)涉及。?
初始化日志的再分析 ?
有了Schema Based XML的相關(guān)知識(shí),就可以對(duì)DispatcherServlet的初始化啟動(dòng)日志做進(jìn)一步的詳細(xì)分析。而這次的分析,我們?cè)噲D弄清楚以下問題:?
引用 [main] INFO /sample - Initializing Spring FrameworkServlet 'dispatcher'?
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定位和加載 (結(jié)束) ##?
## NamespaceHandler執(zhí)行階段 (開始) ##?
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執(zhí)行階段 (結(jié)束) ##?
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
在上面的啟動(dòng)日志中,筆者還是把不同的日志功能使用不同的顏色進(jìn)行了區(qū)分。這里進(jìn)行逐一分析:?
1. 黑色加粗標(biāo)記區(qū)域 —— 容器的啟動(dòng)和結(jié)束標(biāo)志 ?
這個(gè)部分的日志比較明顯,位于容器的啟動(dòng)階段和結(jié)束階段,在之前的討論中我們已經(jīng)分析過,這里不再重復(fù)。 ?
2. 黃色注釋段落 —— Schema定位和加載 ?
這個(gè)部分的日志反應(yīng)出剛才我們所分析的Schema-based XML的工作原理。這是其中的第一步:讀取META-INF/spring.schemas的內(nèi)容,加載schema定義。然后找到相應(yīng)的NamespaceHandler,執(zhí)行其實(shí)現(xiàn)類。 ?
3. 藍(lán)色注釋部分 —— NamespaceHandler執(zhí)行階段 ?
這個(gè)部分的日志,可以幫助我們回答本節(jié)一開始所提出的兩個(gè)問題。絕大多數(shù)的組件,都是在BeanDefinitionParser的實(shí)現(xiàn)類中使用編程的方式注冊(cè)的。 ?
4. 紅色標(biāo)記區(qū)域 —— 組件注冊(cè)細(xì)節(jié) ?
這個(gè)部分的日志區(qū)域徹底回答了本節(jié)一開始所提出的最后一個(gè)問題:一共有18個(gè)組件被注冊(cè),就是紅色標(biāo)記的那18個(gè)bean。 ?
小結(jié) ?
本文所涉及到的話題,主要圍繞著SpringMVC的核心配置問題展開。讀者可以將本文作為上一篇文章的續(xù)篇,將兩者結(jié)合起來閱讀。因?yàn)閺暮暧^上說,本文的話題實(shí)際上也屬于初始化主線的一個(gè)部分。
在文章寫完之后,也陸續(xù)收到了一些反饋,其中比較集中的問題,是有關(guān)WebApplicationContext對(duì)組件進(jìn)行初始化的過程交代的不夠清楚。所以,本文作為上一篇文章的續(xù)文,就試圖來講清楚這個(gè)話題。?
SpringMVC的核心配置文件 ?
SpringMVC的核心配置文件,我們從整個(gè)專欄的第一篇文章就開始接觸。所以,我們?cè)谶@里首先對(duì)SpringMVC的核心配置文件做一些概括性的回顧。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件是構(gòu)成SpringMVC應(yīng)用程序的必要元素之一。
這是我們?cè)谥v有關(guān)SpringMVC的構(gòu)成要素時(shí)就曾經(jīng)提到過的一個(gè)重要結(jié)論。當(dāng)時(shí)我們所說的另外兩大必要元素就是DispatcherServlet和Controller。因而,SpringMVC的核心配置文件在整個(gè)應(yīng)用程序中所起到的作用也是舉足輕重的。這也就是我們?cè)谶@里需要補(bǔ)充對(duì)這個(gè)文件進(jìn)行詳細(xì)分析的原因。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件與傳統(tǒng)的Spring Framework的配置文件是一脈相承的。
這個(gè)結(jié)論很容易理解。作為Spring Framework的一部分,我們可以認(rèn)為SpringMVC是整個(gè)Spring Framework的一個(gè)組件。因而兩者的配置體系和管理體系完全相同也屬情理之中。實(shí)際上,SpringMVC所采取的策略,就是借用Spring Framework強(qiáng)大的容器(ApplicationContext)功能,而絕非自行實(shí)現(xiàn)。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件是架起DispatcherServlet與WebApplicationContext之間的橋梁。
我們?cè)趙eb.xml中指定SpringMVC的入口程序DispatcherServlet時(shí),實(shí)際上蘊(yùn)含了一個(gè)對(duì)核心配置文件的指定過程([servlet-name]-servlet.xml)。當(dāng)然,我們也可以明確指定這個(gè)核心配置文件的位置。這些配置選項(xiàng),我們已經(jīng)在上一篇文章中詳細(xì)介紹過,這里不再重復(fù)。?
而上面這一結(jié)論,除了說明兩者之間的配置關(guān)系之外,還包含了一層運(yùn)行關(guān)系: DispatcherServlet負(fù)責(zé)對(duì)WebApplicationContext進(jìn)行初始化,而初始化的依據(jù),就是這個(gè)SpringMVC的核心配置文件。 所以,SpringMVC的核心配置文件的內(nèi)容解讀將揭開整個(gè)SpringMVC初始化主線的全部秘密。?
如果我們把這個(gè)結(jié)論與上一個(gè)結(jié)論結(jié)合起來來看,也正因?yàn)镾pringMVC的核心配置文件使用了與Spring Framework相同的格式,才使其成為DispatcherServlet駕馭Spring的窗口。?
downpour 寫道 結(jié)論?SpringMVC的核心配置文件是SpringMVC中所有組件的定義窗口,通過它我們可以指定整個(gè)SpringMVC的行為方式。
這個(gè)結(jié)論告訴了我們SpringMVC核心配置文件在整個(gè)框架中的作用。組件行為模式的多樣化,決定了我們必須借助一個(gè)容器(WebApplicationContext)來進(jìn)行統(tǒng)一的管理。而SpringMVC的核心配置文件,就是我們進(jìn)行組件管理的窗口。?
核心配置文件概覽 ?
說了那么多有關(guān)SpringMVC核心配置文件的結(jié)論,我們不妨來看一下這個(gè)配置文件的概況:?
Xml代碼??
這是一個(gè)非常典型的SpringMVC核心配置文件。雖然我們?cè)谶@里幾乎對(duì)每一段重要的配置都做了注釋,不過可能對(duì)于毫無SpringMVC開發(fā)經(jīng)驗(yàn)的讀者來說,這段配置基本上還無法閱讀。所以接下來,我們就試圖對(duì)這個(gè)文件中的一些細(xì)節(jié)加以說明。?
【頭部聲明】 ?
配置文件中首先進(jìn)入我們眼簾的是它的頭部的一大段聲明:?
Xml代碼??
這個(gè)部分是整個(gè)SpringMVC核心配置文件的關(guān)鍵所在。這一段聲明,被稱之為 Schema-based XML的聲明 部分。有關(guān)Schema-based XML的概念,讀者可以參考Spring官方的reference:?
Appendix C. XML Schema-based configuration ?
Appendix D. Extensible XML authoring ?
為了幫助讀者快速理解,我們稍后會(huì)專門開辟章節(jié)針對(duì)Schema-based XML的來龍去脈進(jìn)行講解。?
【組件定義】 ?
除了頭部聲明部分的其他配置部分,就是真正的 組件定義 部分。在這個(gè)部分中,我們可以看到兩種不同類型的配置定義模式:?
1. 基于Schema-based XML的配置定義模式 ?
Xml代碼??
2. 基于Traditional XML的配置定義模式 ?
Xml代碼??
兩種不同的組件定義模式,其目的是統(tǒng)一的: 對(duì)SpringMVC中的組件進(jìn)行聲明,指定組件的行為方式。 ?
雖然兩種不同的組件定義模式的外在表現(xiàn)看上去有所不同,但是SpringMVC在對(duì)其進(jìn)行解析時(shí)最終都會(huì)將其轉(zhuǎn)化為組件的定義而加載到WebApplicationContext之中進(jìn)行管理。所以我們需要理解的是蘊(yùn)藏在配置背后的目的而非配置本身的形式。?
至于這兩種不同的配置形式之間的關(guān)系,我們稍后會(huì)在Schema-based XML的講解中詳細(xì)展開。?
Schema-based XML ?
【基本概念】 ?
Schema-based XML本身并不是SpringMVC或者Spring Framework獨(dú)創(chuàng)的一種配置模式。我們可以看看W3C對(duì)于其用途的一個(gè)大概解釋:?
W3C 寫道 The purpose of an XSD schema is to define and describe a class of XML documents by using schema components to constrain and document the meaning, usage and relationships of their constituent parts: datatypes, elements and their content and attributes and their values. Schemas can also provide for the specification of additional document information, such as normalization and defaulting of attribute and element values. Schemas have facilities for self-documentation. Thus, XML Schema Definition Language: Structures can be used to define, describe and catalogue XML vocabularies for classes of XML documents.
這個(gè)解釋稍微有點(diǎn)抽象。所以我們可以來看看Spring官方reference對(duì)于引入Schema-based XML的說法:?
Spring Reference 寫道 The central motivation for moving to XML Schema based configuration files was to make Spring XML configuration easier. The 'classic' <bean/>-based approach is good, but its generic-nature comes with a price in terms of configuration overhead.
也就是說,我們引入Schema-based XML是為了對(duì)Traditional的XML配置形式進(jìn)行簡(jiǎn)化。 通過Schema的定義,把一些原本需要通過幾個(gè)bean的定義或者復(fù)雜的bean的組合定義的配置形式,用另外一種簡(jiǎn)單而可讀的配置形式呈現(xiàn)出來。 ?
所以,我們也可以由此得出一些有用的推論:?
downpour 寫道 Schema-based XML可以代替Traditional的XML配置形式,在Spring容器中進(jìn)行組件的定義。
這里的代替一詞非常重要,這就意味著傳統(tǒng)的XML配置形式在這里會(huì)被顛覆,我們?cè)趯?duì)Schema-based XML進(jìn)行解讀時(shí),需要使用一種全新的語義規(guī)范來理解。?
downpour 寫道 Schema-based XML可以極大改善配置文件的可讀性并且縮小配置文件的規(guī)模。
這是從引入Schema-based XML的目的反過來得出的推論。因?yàn)槿绻隨chema-based XML之后,整個(gè)配置變得更加復(fù)雜,那么Schema-based XML的引入也就失去了意義。?
同時(shí),筆者在這里需要特別強(qiáng)調(diào)的是Schema-based XML的引入,實(shí)際上是 把原本靜態(tài)的配置動(dòng)態(tài)化、過程化。 有關(guān)這一點(diǎn),我們稍后會(huì)有說明。?
【引入目的】 ?
在早期的Spring版本中,只有Traditional XML一種組件定義模式。當(dāng)時(shí),XML作為Java最好的朋友,自然而然在整個(gè)框架中起到了舉足輕重的作用。根據(jù)Spring的設(shè)計(jì)原則,所有納入WebApplicationContext中管理的對(duì)象,都被映射為XML中的一個(gè)<bean>節(jié)點(diǎn),通過對(duì)于<bean>節(jié)點(diǎn)的一個(gè)完整描述,我們可以有效地將整個(gè)應(yīng)用程序中所有的對(duì)象都納入到一個(gè)統(tǒng)一的容器中進(jìn)行管理。?
這種統(tǒng)一化的描述,帶來的是管理上的便利,不過同時(shí)也帶來了邏輯上的困擾。因?yàn)榻y(tǒng)一的節(jié)點(diǎn),降低了配置的難度,我們幾乎只需要將<bean>節(jié)點(diǎn)與Java的對(duì)象模型對(duì)應(yīng)起來即可。(有一定經(jīng)驗(yàn)的Spring程序員可以回憶一下,我們?cè)诰帉慡pring配置文件時(shí),是否也是一個(gè)將配置選項(xiàng)與Java對(duì)象中屬性或者方法對(duì)應(yīng)起來的過程)但是這樣的配置形式本身并不具備邏輯語義,也就是說我們無法非常直觀地看出某一個(gè)特定的<bean>定義,從邏輯上它到底想說明什么問題??
這也就是后來Schema-based XML開始被引入并流行開來的重要原因。從形式上看,Schema-based XML相比較Traditional XML至少有三個(gè)方面的優(yōu)勢(shì):?
- namespace?—— 擁有很明確的邏輯分類
- element?—— 擁有很明確的過程語義
- attributes?—— 擁有很簡(jiǎn)明的配置選項(xiàng)
?
在圖中,我們分別用上下兩層來說明某一個(gè)配置節(jié)點(diǎn)的結(jié)構(gòu)名稱以及它們的具體作用。由此可見, Schema-based XML中的配置節(jié)點(diǎn)擁有比較鮮明的功能特性,通過namespace、element和attributes這三大元素之間的配合,共同完成對(duì)一個(gè)動(dòng)態(tài)過程的描述。 ?
例如,<mvc:annotation-driven />這段配置想要表達(dá)的意思,就是在mvc的空間內(nèi)實(shí)現(xiàn)Annotation驅(qū)動(dòng)的配置方式。其中,mvc表示配置的有效范圍,annotation-driven則表達(dá)了一個(gè)動(dòng)態(tài)的過程,實(shí)際的邏輯含義是:整個(gè)SpringMVC的實(shí)現(xiàn)是基于Annotation模式,請(qǐng)為我注冊(cè)相關(guān)的行為模式。?
這種配置方式下,可讀性大大提高:我們無需再去理解其中的實(shí)現(xiàn)細(xì)節(jié)。同時(shí),配置的簡(jiǎn)易性也大大提高:我們甚至不用去關(guān)心哪些bean被定義了。?
所以總體來說,Schema-based XML的引入,對(duì)于配置的簡(jiǎn)化是一個(gè)極大的進(jìn)步。?
【構(gòu)成要素】 ?
在Spring中,一個(gè)Schema-based XML有兩大構(gòu)成要素: 過程實(shí)現(xiàn) 和 配置定義 。?
先談?wù)勥^程實(shí)現(xiàn)。所謂過程實(shí)現(xiàn),其實(shí)就是我們剛才所舉的那個(gè)例子中,實(shí)現(xiàn)實(shí)際背后邏輯的過程。這個(gè)過程由兩個(gè)Java接口來進(jìn)行表述:?
- NamespaceHandler —— 對(duì)Schema定義中namespace的邏輯處理接口
- BeanDefinitionParser —— 對(duì)Schema定義中element的邏輯處理接口
Java代碼??
我們可以看到,MvcNamespaceHandler的執(zhí)行,只不過依次調(diào)用了不同的BeanDefinitionParser的實(shí)現(xiàn)類而已,而每一個(gè)BeanDefinitionParser的實(shí)現(xiàn),則對(duì)應(yīng)于Schema定義中的element邏輯處理。例如,AnnotationDrivenBeanDefinitionParser對(duì)應(yīng)于:<mvc:annotation-driven />這個(gè)element實(shí)現(xiàn);ResourcesBeanDefinitionParser則對(duì)應(yīng)于<mvc:resources />的實(shí)現(xiàn)等等。?
所以,要具體了解每個(gè)element的行為過程,只要研究每一個(gè)BeanDefinitionParser的實(shí)現(xiàn)類即可。我們以整個(gè)MVC空間中最重要的一個(gè)節(jié)點(diǎn)<mvc:annotation-driven />為例,對(duì)AnnotationDrivenBeanDefinitionParser進(jìn)行說明,其源碼如下:?
Java代碼??
整個(gè)過程看上去稍顯凌亂,不過我們發(fā)現(xiàn)其中圍繞的一條主線就是: 使用編程的方式來對(duì)bean進(jìn)行注冊(cè)。 也就是說,<mvc:annotation-driven />這樣一句配置,頂上了我們?nèi)绱硕嗟腷ean定義。難怪Schema-based XML被譽(yù)為是簡(jiǎn)化XML配置的絕佳幫手了。?
有了過程實(shí)現(xiàn),我們?cè)賮碚務(wù)勁渲枚x。配置定義的目的非常簡(jiǎn)單,就是通過一些配置文件,將上述的過程實(shí)現(xiàn)類串聯(lián)起來,從而完成整個(gè)Schema-based XML的定義。?
整個(gè)配置定義,也分為兩個(gè)部分:?
- Schema定義?—— 一個(gè)xsd文件,描述整個(gè)Schema空間中element和attribute的定義
- 注冊(cè)配置文件?—— 由META-INF/spring.handlers和META-INF/spring.schemas構(gòu)成,用以注冊(cè)Schema和Handler
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd ?
同時(shí),這個(gè)地址在Spring的發(fā)布包中也存有一個(gè)備份。這個(gè)備份位于SpringMVC的分發(fā)包spring-webmvc的JAR包之中。?
?
這樣做的好處在于,我們?cè)趯?duì)Schema進(jìn)行引用時(shí),可以通過本地尋址來加快加載速度。?
注:如果我們回顧一下之前的核心配置文件中的頭部聲明部分。其中的xsi:schemaLocation聲明就是用于指定映射于本地的XSD文件。所以xsi:schemaLocation的定義不是必須的,不過聲明它能夠使Spring自動(dòng)查找本地的緩存來進(jìn)行schema的尋址。 ?
我們?cè)谶@里不對(duì)XSD文件做過多的內(nèi)容分析,因?yàn)槠渲胁煌夂跏菍?duì)element的定義、attributes的定義等等。這些內(nèi)容是我們進(jìn)行Schema-based XML配置的核心基礎(chǔ)。?
配置定義的另外一個(gè)元素構(gòu)成是META-INF/spring.handlers和META-INF/spring.schemas這兩個(gè)文件。它們同樣位于SpringMVC的分發(fā)包下。當(dāng)我們?cè)赬ML的頭部聲明中引用了相關(guān)的Schema定義之后,Spring會(huì)自動(dòng)查找spring.schemas和spring.handlers的定義,根據(jù)其中指定的NamespaceHandler實(shí)現(xiàn)類加載執(zhí)行。?
有關(guān)這個(gè)過程,我們?cè)谥蟮娜罩痉治鲋羞€會(huì)涉及。?
初始化日志的再分析 ?
有了Schema Based XML的相關(guān)知識(shí),就可以對(duì)DispatcherServlet的初始化啟動(dòng)日志做進(jìn)一步的詳細(xì)分析。而這次的分析,我們?cè)噲D弄清楚以下問題:?
- Where?—— 組件的聲明在哪里?
- How?—— 組件是如何被注冊(cè)的?
- What?—— 究竟哪些組件被注冊(cè)了?
引用 [main] INFO /sample - Initializing Spring FrameworkServlet 'dispatcher'?
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定位和加載 (結(jié)束) ##?
## NamespaceHandler執(zhí)行階段 (開始) ##?
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執(zhí)行階段 (結(jié)束) ##?
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
在上面的啟動(dòng)日志中,筆者還是把不同的日志功能使用不同的顏色進(jìn)行了區(qū)分。這里進(jìn)行逐一分析:?
1. 黑色加粗標(biāo)記區(qū)域 —— 容器的啟動(dòng)和結(jié)束標(biāo)志 ?
這個(gè)部分的日志比較明顯,位于容器的啟動(dòng)階段和結(jié)束階段,在之前的討論中我們已經(jīng)分析過,這里不再重復(fù)。 ?
2. 黃色注釋段落 —— Schema定位和加載 ?
這個(gè)部分的日志反應(yīng)出剛才我們所分析的Schema-based XML的工作原理。這是其中的第一步:讀取META-INF/spring.schemas的內(nèi)容,加載schema定義。然后找到相應(yīng)的NamespaceHandler,執(zhí)行其實(shí)現(xiàn)類。 ?
3. 藍(lán)色注釋部分 —— NamespaceHandler執(zhí)行階段 ?
這個(gè)部分的日志,可以幫助我們回答本節(jié)一開始所提出的兩個(gè)問題。絕大多數(shù)的組件,都是在BeanDefinitionParser的實(shí)現(xiàn)類中使用編程的方式注冊(cè)的。 ?
4. 紅色標(biāo)記區(qū)域 —— 組件注冊(cè)細(xì)節(jié) ?
這個(gè)部分的日志區(qū)域徹底回答了本節(jié)一開始所提出的最后一個(gè)問題:一共有18個(gè)組件被注冊(cè),就是紅色標(biāo)記的那18個(gè)bean。 ?
小結(jié) ?
本文所涉及到的話題,主要圍繞著SpringMVC的核心配置問題展開。讀者可以將本文作為上一篇文章的續(xù)篇,將兩者結(jié)合起來閱讀。因?yàn)閺暮暧^上說,本文的話題實(shí)際上也屬于初始化主線的一個(gè)部分。
總結(jié)
以上是生活随笔為你收集整理的SpringMVC深度探险 —— SpringMVC核心配置文件详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3精要(8)-对象,变量,引
- 下一篇: 绘制扇形的多种方式,包括border-r