javascript
Spring如何加载XSD文件
http://blog.csdn.net/bluishglc/article/details/7596118
本文原文連接:?http://blog.csdn.net/bluishglc/article/details/7596118 ,轉載請注明出處!
有時候你會發現過去一直啟動正常的系統,某天啟動時會報出形如下面的錯誤:
[plain] view plaincopyprint?
很顯然,spring xml配置文件中指定的xsd文件讀取不到了,原因多是因為斷網或spring的官網暫時無法連接導致的。 你可以通過在瀏覽器輸入xsd文件的URL,如:http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 進行確認。
關于這個問題,網上有兩種常見的解決方法,第一種簡單有效,但是工作量大,即:把所有spring配置文件中url形式的xsd路徑轉換成指向本地xsd文件的classpath形式的路徑,例如:classpath:org/springframework/beans/factory/xml/spring-beans-2.5.xsd ,再有一種方法就是在本機搭建web服務器,按URL創建相應文件夾,放入對應xsd文件,在本機hosts文件中加入"127.0.0.1 www.springframework.org".實際上,這兩種方法都屬于“歪打正著”式的方法,直正弄明白這一問題還需要從spring的XSD文件加載機制談起。
首先:你必須知道一點:spring在加載xsd文件時總是先試圖在本地查找xsd文件(spring的jar包中已經包含了所有版本的xsd文件),如果沒有找到,才會轉向去URL指定的路徑下載。這是非常合理的做法,并不像看上去的那樣,每次都是從站點下載的。事實上,假如你的所有配置是正確定的,你的工程完全可以在斷網的情況下啟動而不會報上面的錯誤。Spring加載xsd文件的類是PluggableSchemaResolver,你可以查看一下它的源碼來驗證上述說法。另外,你可以在log4j.xml文件中加入:
[html] view plaincopyprint?
通過日志了解spring是何加載xsd文件的。
接下來,問題就是為什么spring在本地沒有找到需要的文件,不得不轉向網站下載。關于這個問題,其實也非常簡單。在很多spring的jar包里,在META-INF目錄下都有一個spring.schemas,這是一個property文件,其內容類似于下面:
[plain] view plaincopyprint?
實際上,這個文件就是spring關于xsd文件在本地存放路徑的映射,spring就是通過這個文件在本地(也就是spring的jar里)查找xsd文件的。那么,查找不到的原因排除URL輸入有誤之外,可能就是聲明的xsd文件版本在本地不存在。一般來說,新版本的spring jar包會將過去所有版本(應該是自2.0以后)的xsd打包,并在spring.schemas文件中加入了對應項,出現問題的情況往往是聲明使用了一個高版本的xsd文件,如3.0,但依賴的spring的jar包卻是2.5之前的版本,由于2.5版本自然不可能包含3.0的xsd文件,此時就會導致spring去站點下載目標xsd文件,如遇斷網或是目標站點不可用,上述問題就發生了。
但是,在實現開發中,出現上述錯誤的幾率并不高,最常見的導致這一問題的原因其實與使用了一個名為“assembly”的maven打包插件有關。很多項目需要將工程連同其所依賴的所有jar包打包成一個jar包,maven的assembly插件就是用來完成這個任務的。但是由于工程往往依賴很多的jar包,而被依賴的jar又會依賴其他的jar包,這樣,當工程中依賴到不同的版本的spring時,在使用assembly進行打包時,只能將某一個版本jar包下的spring.schemas文件放入最終打出的jar包里,這就有可能遺漏了一些版本的xsd的本地映射,進而出現了文章開始提到的錯誤。如果你的項目是打成單一jar的,你可以通過檢查最終生成的jar里的spring.schemas文件來確認是不是這種情況。而關于這種情況,解決的方法一般是推薦使用另外一種打包插件shade,它確實是一款比assembly更加優秀的工具,在對spring.schemas文件處理上,shade能夠將所有jar里的spring.schemas文件進行合并,在最終生成的單一jar包里,spring.schemas包含了所有出現過的版本的集合!
以上就是spring加載XSD文件的機制和出現問題的原因分析。實際上,我們應該讓我們工程在啟動時總是加載本地的xsd文件,而不是每次去站點下載,做到這一點就需要你結合上述提及的種種情況對你的工程進行一番檢查。
?
?
==========================
http://blog.csdn.net/javabenface/article/details/7441923
以前一直沒注意spring對xml的解析過程,
特別是xml文件頭上的一堆xmlns:
| 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <?XML:NAMESPACE PREFIX = [default] http://www.springframework.org/schema/beans NS = "http://www.springframework.org/schema/beans" /><beans xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context ?????????????????????????? http://www.springframework.org/schema/context/spring-context-3.0.xsd" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| 3 | </beans> |
這些命名空間中是怎么解析的,
大概可以分為下面這個步驟:
1. 解析XML, 找到所有的 命名空間 如: http://www.springframework.org/schema/context
2. 在Classpath中查找所有的 spring.handlers 并解析其中配置的 命名空間 與 對應的處理類, 如:
3. 根據查找到的處理Handler去解析配置文件中相應的結點.
而命名空間對應的xsd文件, 則是在 spring.schemas 中指定的, 如:
http\://www.springframework.org/schema/aop/spring-aop-2.0.xsd=org/springframework/aop/config/spring-aop-2.0.xsd http\://www.springframework.org/schema/aop/spring-aop-2.5.xsd=org/springframework/aop/config/spring-aop-2.5.xsd http\://www.springframework.org/schema/aop/spring-aop-3.0.xsd=org/springframework/aop/config/spring-aop-3.0.xsd http\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-3.0.xsd?
?
?
====================
http://kiminotes.iteye.com/blog/547103
本文通過對 spring 源代碼的閱讀,解析 spring 如何校驗、解析 xml 格式的 bean 配置文件,還會介紹如何擴展 spring,這也是我閱讀這部分代碼的原因所在。
在使用 spring 的時候,最常用的配置 bean 的方式就是 xml 文件,spring 本身提供了很多 xml namespace 的配置,如 jms、aop 等。我在使用這些 spring 內建的 namespace(其實也就用過 jms 這個namesapce) 的時候并沒有覺得有什么疑問或奇怪的,但當我使用 activemq 以及了解 mule 的時候發現,它們的配置都是基于 spring 構建的,只是提供了它們自己的 namespace 而已。我很迷惑,它們這是怎么實現的呢,難道這些框架本身也替換了 spring 實現的解析 spring bean xml 配置文件的實現,想想也沒這必要,它們完全可以定義自己的 xml 配置文件格式,然后自己實現解析,為什么要嵌入到 spring 中呢?感覺很奇怪。帶著這個疑問,我就在查看了 spring 提供的 reference,在 spring reference 的 appendix B - Extensible XML authoring 中發現,原來可以通過 xml schema 來擴展 spring,并通過一些配置可以使 spring 識別并處理這些配置。這是疑問又出來了,spring 是怎么實現的呢?于是決定讀讀源代碼,看看是如何實現的。為了節省時間,我從 XmlBeanFactory 開始,如果從 ApplicationContext 的實現開始的話,會遇到很多干擾,因為 ApplicationContext 比 BeanFactory 提供了更多的更強大的功能。
spring 使用 XmlBeanDefinitionReader 來讀取并解析 xml 文件,XmlBeanDefinitionReader 是 BeanDefinitionReader 接口的實現。BeanDefinitionReader 定義了 spring 讀取 bean 定義的一個接口,這個接口中有一些 loadBeanDefinitions 方法,從它們的方法簽名可知,spring 把讀取 bean 配置的來源抽象為 Resource 接口。BeanDefinitionReader 接口有兩個具體的實現,其中之一就是從 xml 文件中讀取配置的 XmlBeanDefinitionReader,另一個則是從 java properties 文件中讀取配置的 PropertiesBeanDefinitionReader。開發人員也可以提供自己的 BeanDefinitionReader 實現,根據自己的需要來讀取 spring bean 定義的配置。在 XmlBeanFactory 中創建了 XmlBeanDefinitionReader 的實例,并在 XmlBeanFactory 的構造方法中調用了 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法,由 loadBeanDefinitions 方法負責加載 bean 配置并把 bean 配置注冊到 XmlBeanFactory 中。
loadBeanDefinitions 方法首先要通過 Resource 接口讀取 xml 配置文件,并把它讀到一個 Document 對象中,用于解析,這個動作是由接口 DocumentLoader 的實現來完成的。spring 有一個默認實現 DefaultDocumentLoader。對于如何讀取一個 xml 文件為 Document 對象,我想大部分都很熟悉:創建 DocumentBuilderFactory,由 DocumentBuilderFacoty 創建 DocumentBuidler,調用 DocumentBuilder 的 parse 方法把文件或流解析為 Document。的確 spring 也是這樣做的,但有一點不要忘記,spring 需要使用 xml schema 來驗證 xml,spring 使用的 jaxp 1.2 中提供的 xml schema 驗證方式,并沒有使用 jaxp 1.3 中引入的 Schema 對象來驗證(jboss cache 也是使用的這種方式)。DefaultDocumentLoader 在創建了 DocumentBuilderFactory 對象后會判斷當前是否使用 xml schema 驗證,如果是則會在 DocumentBuiderFactory 上設置一個屬性,這個屬性名為 http://java.sun.com/xml/jaxp/properties/schemaLanguage,如果把這個屬性設置為 http://www.w3.org/2001/XMLSchema,jaxp 則會使用 xml schema 來驗證 xml 文檔,使用這種驗證方式需要提供一個 EntityResolver 的實現,EntityResolver 的使用 DocumentBuilder 的 setEntityResolver 方法設置。spring 提供了 EntityResolver 的實現,這個實現也是擴展 spring 的關鍵所在,這個后面會提到,暫且略過。
在完成了 Resource 到 Document 的轉換后,下面就是從 Document 中解析出各個 bean 的配置了,為此 spring 又抽象了一個接口 BeanDefinitionDocumentReader,從它的名稱中可以一目了然這個接口負責從 Document 中讀取 bean 定義,這個接口中只定義了一個方法 registerBeanDefinitions。spring 也提供了一個默認實現 DefaultBeanDefinitionDocumentReader。DefaultBeanDefinitionDocumentReader 主要完成兩件事情,解析 <bean> 元素,為擴展 spring 的元素尋找合適的解析器,并把相應的元素交給解析器解析。第一個任務,解析 <bean> 元素,這個 spring 的核心功能及 IoC 或者是 DI,這由 spring 自己來處理,這個工作有一個專門的委托類來處理 BeanDefinitionParserDelegate,由它來解析 <bean> 元素,并把解析的結果注冊到 BeanDefinitionRegistry(XmlBeanFactory 實現了此接口) 中。那么 spring 如何來區別 bean 元素以及其它擴展元素的,大家可能很自然地就能想到使用元素名啊,的確使用元素名可以處理,但這就會出現這樣的情況,程序員 A 擴展 spring 定一個元素名為 c 的元素,同樣程序員 B 擴展 spring 也定義了名為 c 的元素,此時就無法區分了。其實 spring 是通過 xml namespace 來區分的,同樣查找擴展元素的解析器也是通過 xml namespace 來處理的。spring 從根元素開始,在解析每個元素的時候,都會先查詢元素的 namespace uri,如果元素的 namespace uri 為 http://www.springframework.org/schema/beans,則由 spring IoC 來解析處理,這些元素包括 beans、bean、import、alias,如果 namespace uri 不是 http://www.springframework.org/schema/beans,則會使用 NamespaceHandlerResolver 來解析出一個 NamespaceHandler,使用 NamespaceHandler 來解析處理這個元素。NamespaceHandlerResovler 和 NamespaceHandler 就是擴展 spring 的秘密所在。NamespaceHandlerResolver 是一個接口,spring 使用與 EntityResolver 相同的策略來實現,這個后面會提到。當這一步完成了 spring 也就完成了讀取解析 xml 配置。
上面僅僅是根據代碼的執行路徑簡單地描述了 spring 解析 xml 配置的一個大體過程,至于更細節性的東西,感興趣的朋友不妨根據我上面的思路,詳細的讀一讀這一部分的源代碼。(我閱讀源代碼都是借助于 ide 來完成的,這能提搞閱讀的速度,如果碰到一些是在不理解的代碼還可以寫一些非常簡單的 demo 單步執行,看看代碼到底是如何執行的)
下面介紹一下如何擴展 spring。不知大家有沒有注意到,在 spring 的源代碼目錄中有兩個很特殊的文件:spring.schemas 和 spring.handlers,這兩個文件以及 spring 中對 EntityResolver 和 NamespaceHandlerResolver 的實現 PluggableSchemaResolver 和 DefaultNamespaceHandlerResolver 是擴展 spring 的關鍵所在。其實 spring.schemas 和 spring.handlers 文件是標準的 java properties 文件。這兩個文件都被大包到 spring jar 包中的 META-INF 目錄中,PluggableSchemaResolver 通過讀取 spring.schemas 文件,根據 xml 文件中實體的 system id 來解析這些實體,大家可以看一下 spring.schemas 文件中的 key 就可以知道 system id 是什么了(其實我也不知道 system id 和 public id 是啥,知道的朋友不妨在文后的回復中給我留言,謝謝);而 DefaultNamespaceHandlerResolver 則是根據元素的 namespace uri 在 spring.handlers 文件中查找具體的 NamespaceHandler 的實現。
如上面所提到的,擴展 spring 需要完成以下幾個工作,定義一個 xml schema,并編寫相應的 spring.schemas 文件,實現 NamespaceHandler 接口,根據需要還可能需要實現 BeanDefinitionParser 和 BeanDefinitionDecorator 等接口,更詳細的信息可以參考 spring 的 reference 或者其他 spring 相關的文檔,由于我想了解的事情已經了解了,就沒有繼續研究下去。
提到 擴展 spring 這里簡單地說一下 activemq。說 activemq 通過擴展 spring 來實現本身的配置其實并不完全正確,事實上 activemq 并沒有擴展 spring,完全使用 spring 的 IoC 配置 activemq 也是可以實現的。activemq 本身的架構是基于 pojo 的,所以使用 spring 的 IoC 完成可以完成 activemq 的配置任務。但 spring 的 bean 配置看上去有點“專業化”,尤其是第一次看到 spring 配置的時候,會困惑與 property 元素(其實當了解了 spring 之后也是很好理解的)。開源社區里不知道是哪位神人開發了?xbean?這樣一個框架。這個框架具體做什么呢,它主要完成三件事情,第一根據源代碼中的一些特殊的 doclet 生成一個 xml schema,看看 activemq 的源代碼,大家可能會發現,很多類的 javadoc 中多了這樣一個 tag @org.apache.xbean.XBean 以及其它的一些 tag,xbean 會根據這些特殊的 tag 來生成一個 xml schema;xbean 完成的第二件事情就是它會生成擴展 spring 所需的一些配置;第三它重新實現了一些 spring 中的可替換組件,如它擴展了 XmlBeanDefinitionReader 實現了自己的 BeanDefinitionReader XBeanXmlDefinitionReader,實現了自己的 ApplicationContext ResourceXmlApplicationContext,如果使用了 xbean 就必須使用 xbean 實現的 ApplicationContext。xbean 提供的 BeanDefinitionReader 實現只是把一些定制的元素轉換成了 spring 中的 bean 元素,這樣使 spring 的配置更容易閱讀和理解。
本文簡單地介紹了 spring 解析 xml 配置以及擴展 spring 方面的一些內容,順帶還介紹了一下 xbean,感興趣的朋友可以詳細地讀一下這部分的源代碼。文中的解析并沒有上升到什么架構或設計的高度,說實話自己還沒有達到這樣的高度,僅僅是對源代碼執行路徑的一個簡單描述,如果能使各位看官對 spring 的內部實現有一點的了解,或者使您對 spring 的內部實現感興趣并希望進一步了解,能達到這樣的效果我已經很知足了,別無他求。如果您有關于這方面的自己的想法不妨與大家一同分享。如果文中有言之有誤的地方,還望各位指正。
?
總結
以上是生活随笔為你收集整理的Spring如何加载XSD文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xmlns:xsi=http://www
- 下一篇: XSLT简单教程--XSLT的实例