1. 程式人生 > >Spring學習筆記(三)基於XML Schema的配置方式

Spring學習筆記(三)基於XML Schema的配置方式

前言:Spring2.0開始,Spring允許使用基於XML Schema的配置方式來簡化Spring配置檔案,這種方式更加簡潔,可以對Spring配置檔案進行“減肥”。
  Spring配置檔案的基本配置的<beans>標籤包含如下配置:

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
</beans>

  其中已經包含了一個最基本的Schema:spring-beans-4.0.xsd,也是預設的名稱空間。如果對XML名稱空間不瞭解的可以從參考:XML學習筆記

一、Spring中的Schema機制解析

  Spring在啟動時是要檢驗XML檔案的,如果我們開啟上面的xmlns後面的連結,不難發現那是儲存各個版本spring-beans-*.xsd的遠端文件。如果斷網,name就無法訪問這些檔案了,Spring已經將此情況考慮進去了,Spring預設從本地載入XSD檔案。

  首先說明Spring建立Schema的基本過程:

  1. 設計配置屬性和JavaBean。也就是設計好配置項,並通過JavaBean來建模。
  2. 編寫XSD檔案。(詳細可參考:Schema 教程
  3. 編寫NamespaceHandler和BeanDefinitionParser完成解析工作
  4. 編寫spring.handlers和spring.schemas串聯起所有部件
  5. 在Bean檔案中應用。

  下面重點說明一下第四/五步,第四步是編寫NamespaceHandler和BeanDefinitionParser完成解析工作,具體說來 NamespaceHandler會根據schema和節點名找到某個BeanDefinitionParser,然後由 BeanDefinitionParser完成具體的解析工作,開發好的handler與xsd還沒法讓應用感知到,這就需要將它們串聯起來。第五步是編寫spring.handlers和spring.schemas串聯起所有部件

,這兩個檔案的地址必須是META-INF/spring.handlers和META-INF/spring.schemas,spring會預設去 載入它們

<1> spring.handlers

  spring.handlers檔案官網解釋:The properties file called 'spring.handlers' contains a mapping of XML Schema URIs to namespace handler classes.

http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler

  The ':' character is a valid delimiter in the Java properties format, and so the':' character in the URI needs to be escaped with a backslash.
  The first part (the key) of the key-value pair is the URI associated(關聯) with your custom(自定義) namespace extension(拓展), and needs to match(匹配) exactly(精確地) the value of the'targetNamespace'attribute(屬性) as specified in your custom XSD schema.

  也就是說spring.handlers檔案儲存的是XML Schema的URI與NamespaceHandler類的對映關係,這是一個property檔案,因此是kye-value的形式,其中key是我們在XSD Schema中'targetNamespace'屬性所指定的名稱空間的名字,其中冒號需要轉義,而其對應的值就是解析這個名稱空間的NamespaceHandler。

<2> spring.schemas

  spring.schemas檔案官網解釋:The properties file called 'spring.schemas' contains a mapping of XML Schema locations(referred to along with the schema declaration in XML files that use the schema as partof the'xsi:schemaLocation' attribute) to classpath resources. This file is neededto prevent Spring from absolutely having to use a defaultEntityResolver that requiresInternet access to retrieve the schema file. If you specify the mapping in thisproperties file, Spring will search for the schema on the classpath (in this case'myns.xsd' in the'org.springframework.samples.xml' package):

http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd

  The upshot of this is that you are encouraged to deploy your XSD file(s) right alongsidetheNamespaceHandler andBeanDefinitionParser classes on the classpath.
  也就是說spring.handlers包含的是XML Schema檔案本地儲存的對映關係,其鍵值對中的值都可以在檔案中找到。這個檔案中儲存的鍵都是可以在引用的XML檔案中的xsi:schemaLocation屬性中引用。

注意:在該檔案中有一個沒有指定版本的特殊鍵值對,這也就是說沒有指定版本的時候,會指定這個屬性所指定的對應版本,一般是當前版本的版本號。

1.1 Spring的XML校驗

  使用xmlns指定名稱空間,其中起別名是很常見的例如:

xmlns:p="http://www.springframework.org/schema/p

  這樣在下面以p冒號開頭的都是使用的是這個名稱空間中,可以看出這個網址的最後面的符號與別名是相同的;不知你們有沒有這個疑問,加入我前面的別名與網址最後的民稱不一樣例如上面的名稱空間我寫成如:xmlns:p_text="http://www.springframework.org/schema/p,經過測試能使用,前面只不過是一個引用的別名,但是不推薦這樣,還是寫成前後都一致的比較好,這樣便於理解,參考資料也都是這樣寫的,別搞另類了。

  通常情況下,namespace對應的URI是一個存放XSD的地址,儘管規範沒有這麼要求。如果沒有提供schemaLocation,那麼Spring的XML解析器會從namespace的URI里加載XSD檔案。我們可以把配置檔案改成這個樣子,也是可以正常工作的:

<beans xmlns="http://www.springframework.org/schema/beans/spring-beans.xsd"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  

  schemaLocation屬性提供了一個xml namespace到對應的XSD檔案的一個對映,所以我們可以看到,在xsi:schemaLocation後面配置的字串都是成對的,前面的是namespace的URI,後面是xsd檔案的URI。比如:

    xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans.xsd  
    http://www.springframework.org/schema/security  
    http://www.springframework.org/schema/security/spring-security.xsd"  

注:The 'xsi:schemaLocation' fragment is not actually required, but can be included to reference a local copy of a schema (which can be useful during development).

1.2 Spring的namespace的列表

Spring core

  • aop - AopNamespaceHandler
  • c - SimpleConstructorNamespaceHandler
  • cache - CacheNamespaceHandler
  • context - ContextNamespaceHandler
  • jdbc - JdbcNamespaceHandler
  • jee - JeeNamespaceHandler
  • jms - JmsNamespaceHandler
  • lang - LangNamespaceHandler
  • mvc - MvcNamespaceHandler
  • oxm - OxmNamespaceHandler
  • p - SimplePropertyNamespaceHandler
  • task - TaskNamespaceHandler
  • tx - TxNamespaceHandler
  • util - UtilNamespaceHandler

Spring Security

  • security - SecurityNamespaceHandler
  • oauth - OAuthSecurityNamespaceHandler

Spring integration

  • int - IntegrationNamespaceHandler
  • amqp - AmqpNamespaceHandler
  • event - EventNamespaceHandler
  • feed - FeedNamespaceHandler
  • file - FileNamespaceHandler
  • ftp - FtpNamespaceHandler
  • gemfire - GemfireIntegrationNamespaceHandler
  • groovy - GroovyNamespaceHandler
  • http - HttpNamespaceHandler
  • ip - IpNamespaceHandler
  • jdbc - JdbcNamespaceHandler
  • jms - JmsNamespaceHandler
  • jmx - JmxNamespaceHandler
  • mail - MailNamespaceHandler
  • redis - RedisNamespaceHandler
  • rmi - RmiNamespaceHandler
  • script - ScriptNamespaceHandler
  • security - IntegrationSecurityNamespaceHandler
  • sftp - SftpNamespaceHandler
  • stream - StreamNamespaceHandler
  • twitter - TwitterNamespaceHandler
  • ws - WsNamespaceHandler
  • xml - IntegrationXmlNamespaceHandler
  • xmpp - XmppNamespaceHandler

  可以檢視對應的JavaDoc文件進行檢視。

二、常用詳細解析

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
</beans>

  這是最基本的根元素配置,我們不難發現其中預設的名稱空間是xmlns="http://www.springframework.org/schema/beans",我們找到到spring-beans-*.jar包下的META-INF/spring.handlers檔案,如下:

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

  可以看出,其中已經包含了三個名稱空間,我們不要誤解為:如果我們使用這三個名稱空間,則無須指定其XSD檔案的位置,直接使用xmlns屬性指定別名即可使用。

  其中前兩個確實是不用指定特定的,但是第三個util是需要指定對應的XSD檔案的。

注:the p-namespace and c-namespace is not defined in an XSD file and exists only in the core of Spring。也就是說p和c名稱空間並沒有在XSD檔案中定義,而是直接存在於Spring核心中。

注:Spring框架解壓縮包的schema包含了所有Spring的XML Schema檔案。官網參考文件可參考:VIII. Appendices -> 40. XML Schema-based configuration

2.1 使用p-namespace簡化XML配置

官方使用文件可參考(spring-framework-4.2.5.RELEASE-dist):III. Core Technologies -> 6.4. Dependencies -> 6.4.2. Dependencies and configuration in detail -> XML shortcut with the p-namespace

  使用p-namespace前,需要匯入XML Schema裡的p-namespace,只需要在<beans.../>標籤中新增xmlns:p="http://www.springframework.org/schema/p"即可,如下:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
...
</beans>

  前面所提及的設值注入,如下:

<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">  
    <!-- 驅動呼叫chinese的setAxe()方法,將容器中stoneAxe作為傳入引數 -->  
    <property name="axe" ref="stoneAxe"/>  
</bean>  
<!-- 配置stoneAxe例項,其實現類是StoneAxe -->  
<bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>  
<!-- 配置steelAxe例項,其實現類是SteelAxe -->  
<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/> 

  我們使用p-namespace的話可以達到同樣的效果,如下:

<!-- 驅動呼叫chinese的setAxe()方法,將容器中stoneAxe作為傳入引數 --> 
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
	p:axe-ref="stoneAxe"/>  
<!-- 配置stoneAxe例項,其實現類是StoneAxe -->  
<bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>  
<!-- 配置steelAxe例項,其實現類是SteelAxe -->  
<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/> 

  首先從行數,程式碼量上來說後者減少了很多,其次其更加清晰明瞭,減少了XML的層次,變子元素為屬性,而且將原本屬於子元素的兩個屬性只使用一個元素就可以表示。

  使用也非常簡單:如果需要注入的引數不需要引用容器中另一個已存在的bean例項,則直接使用該需要注入的欄位的名稱,作為屬性的key;否則,則需要在注入欄位的名稱後面加上“-ref”,然後作為屬性的key。如上面示例中需要注入的欄位名為axe,因為其引用了另外一個Bean,所以在axe後面新增上了“-ref”即可。

注意:使用p-namespace沒有標準的XML格式靈活,如果某個Bean屬性名以“-ref”結尾的,那麼採用p-namespace定義時就會發生衝突,而採用標準的XML格式定義則不會出現這種問題。

2.2 使用c-namespace簡化XML配置

官方使用文件可參考(spring-framework-4.2.5.RELEASE-dist):III. Core Technologies -> 6.4. Dependencies -> 6.4.2. Dependencies and configuration in detail -> XML shortcut with the c-namespace

  c-namespace與p-namespace這兩個名稱空間是直接存在於Spring核心中的,p-namespace主要是用於簡化設值注入,而c-namespace則用於簡化構造注入。

  使用方法與p-namespace類同,首先在<beans.../>中新增:xmlns:c="http://www.springframework.org/schema/c",示例如下:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:c="http://www.springframework.org/schema/c"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
	<!-- 配置chinese例項,其實現類是Chinese -->
	<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
		c:axe-ref="steelAxe" c:age="29"/>
	<!-- 配置chinese例項,其實現類是Chinese -->
	<bean id="chinese2" class="org.crazyit.app.service.impl.Chinese"
		c:_0-ref="steelAxe" c:_1="29"/>
	<!-- 配置stoneAxe例項,其實現類是StoneAxe -->
	<bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
	<!-- 配置steelAxe例項,其實現類是SteelAxe -->
	<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
</beans>

  上面配置方式是在c:後使用構造器引數名來指定構造器引數,Spring還支援一種通過索引來配置構造器引數的方式。上面的Bean也可改寫為如下形式:

<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
		c:_0-ref="steelAxe" c:_1="29"/>

  程式碼中c:_0-ref指定使用容器中已有的steelAxe Bean作為第一個構造器引數,c:_1="29"則指定使用29作為第二個構造器引數。在這種方式下,c:_N代表第幾個構造器引數。
注意: the c-namespace, newly introduced in Spring 3.1。c-namespace是Spring3.1才新增的。

2.3 util schema

  As the name implies, the util tags deal with common, utility configuration issues, such as configuring collections, referencing constants, and suchlike.

  顧名思義,util標籤處理常見的、實用的配置問題,例如配置集合,引用常量等。

  To use the tags in the util schema, you need to have the following preamble at the top of your Spring XML configuration file; the text in the snippet below references the correct schema so that the tags in the util namespace are available to you.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- bean definitions here -->

</beans>

  在util Schema下提供瞭如下幾個元素:

  • constant:該元素用於獲取指定類的靜態Field的值。它是FieldRetrievingFactoryBean的簡化配置
  • property-path:該元素用於獲取指定物件的getter方法的返回值。它是PropertyPathFactory的簡化配置
  • List:該元素用於定義一個List Bean,支援使用<value.../>、<ref.../>、<bean.../>等子元素來定義List集合元素。使用該標籤支援如下三個屬性:
    • id:該屬性指定定義一個名為id的List Bean例項
    • list-class:該屬性指定Spring使用哪個List實現類來建立Bean例項。預設使用ArrayList作為實現類
    • scope:指定該List Bean例項的作用域
  • Set:該元素用於定義一個Set Bean,支援使用<value.../>、<ref.../>、<bean.../>等子元素來定義Set集合元素。使用該標籤支援如下三個屬性:
    • id:該屬性指定定義一個名為id的Set Bean例項
    • set-class:該屬性指定Spring使用哪個Set 實現類來建立Bean例項。預設使用HashSet作為實現類
    • scope:指定該Set Bean例項的作用域
  • Map:該元素用於定義一個Map Bean,支援使用<entry.../>來定義Map的key-value對。使用該標籤支援如下三個屬性:
    • id:該屬性指定定義一個名為id的Map Bean例項
    • map-class:該屬性指定Spring使用哪個Map實現類來建立Bean例項。預設使用HashMap作為實現類
    • scope:指定該Map Bean例項的作用域
  • properties:該元素用於載入一份資原始檔,並根據載入的資原始檔建立一個Properties Bean例項。使用該標籤可指定如下幾個屬性:
    • id:該屬性指定定義一個名為id的Properties Bean例項
    • location:指定資原始檔的位置
    • scope:指定該Properties Bean的作用域

示例如下:

程式碼詳見:code\07\7.11\util

其配置檔案如下:

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置檔案的根元素和Schema
	匯入p:名稱空間和util:名稱空間的元素 -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:utilss="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/util
	http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	<!-- 配置chinese例項,其實現類是Chinese -->
	<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
		p:age-ref="chin.age" p:axe-ref="stoneAxe"
		p:schools-ref="chin.schools"
		p:axes-ref="chin.axes"
		p:scores-ref="chin.scores"/>
	<!-- 使用util:constant將指定類的靜態Field定義成容器中的Bean -->
	<utilss:constant id="chin.age" static-field=
		"java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
	<!-- 使用util.properties載入指定資原始檔 -->
	<utilss:properties id="confTest"
		location="classpath:test_zh_CN.properties"/>
	<!-- 使用util:list定義一個List集合,指定使用LinkedList作為實現類,
	如果不指定預設使用ArrayList作為實現類 -->
	<utilss:list id="chin.schools" list-class="java.util.LinkedList">
		<!-- 每個value、ref、bean...配置一個List元素 -->
		<value>小學</value>
		<value>中學</value>
		<value>大學</value>
	</utilss:list>
	<!-- 使用util:set定義一個Set集合,指定使用HashSet作為實現類,
	如果不指定預設使用HashSet作為實現類-->
	<utilss:set id="chin.axes" set-class="java.util.HashSet">
		<!-- 每個value、ref、bean...配置一個Set元素 -->
		<value>字串</value>
		<bean class="org.crazyit.app.service.impl.SteelAxe"/>
		<ref bean="stoneAxe"/>
	</utilss:set>
	<!-- 使用util:map定義一個Map集合,指定使用TreeMap作為實現類,
	如果不指定預設使用HashMap作為實現類 -->
	<utilss:map id="chin.scores" map-class="java.util.TreeMap">
		<entry key="數學" value="87"/>
		<entry key="英語" value="89"/>
		<entry key="語文" value="82"/>
	</utilss:map>
	<!-- 配置steelAxe例項,其實現類是SteelAxe -->
	<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
	<!-- 配置stoneAxe例項,其實現類是StoneAxe -->
	<bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
</beans>

2.4 其它Spring 其它常用的簡化Schema

  暫時只用到了上面的功能,這部分詳細解析待後期進行進一步深入,在此簡要說明一下幾個常用的簡化Schema:

  • spring-aop-*.xsd:用於簡化Spring AOP配置的Schema
  • spring-jee-*.xsd:用於簡化Spring Java EE配置的Schema
  • spring-jms-*.xsd:用於簡化Spring關於JMS配置的Schema
  • spring-lang-*.xsd:用於簡化Spring 動態語言配置的Schema
  • spring-tx-*.xsd:用於簡化Spring 事務配置的Schema

參考資料:

讚賞