Spring 2.0版本支持擴展XML配置,著實興奮了一下,在我看來,Spring作為目前最流行的框架,不能擴展用戶自定義的配置,實在是Spring的一個很不爽的地方,<bean />的方式用起來比較通用,起碼到目前為止符合大部分人的使用習慣,并且能完成Spring所有的配置操作,但是對于第三方的提供商或則會經常擴展Spring功能的開發者來說,使用<bean />這樣的配置方式或許不是他們最想要的,他們需要使組件的配置更加直觀、易閱讀、易擴展……試想使用下面的配置方式
代碼
-
<mytag:datasource?id=
"datasource"
? ??
-
???????databaseType=
"oracle"
? ??
-
???????ip=
"192.168.1.110"
? ??
-
???????port=
"1521"
? ??
-
???????databaseName=
"myDB"
??
-
???????userName=
"admin"
??
-
???????password=
"password"
??
-
???????/> ??
-
??
-
<mytag:ehCache?id=
"ehcache"
? ??
-
???????cache=
"true"
? ??
-
???????maxElements=
"100000"
? ??
-
???????timeToIdleSeconds=
"120"
? ??
-
???????timeToLiveSeconds=
"120"
??
-
???????overflowToDisk=
"true"
??
-
???????/> ??
上面的代碼中配置了兩個組件,datasource和cache組件,相比普通的bean&propertiy方式,很顯然,這種配置方式更直觀,更易讀,更重要的是,如果作為組件發布,使用者也可以很方便快捷的開始使用,而不需要關心組件的任何實現細節。
擴展XML配置大致需要一下幾個步驟 1、創建一個需要擴展的組件 2、定義一個xsd文件描述組件內容 3、創建一個文件,實現BeanDefinitionParser接口,用來解析xsd文件中的定義和組件定義 4、創建一個Handler文件,擴展自NamespaceHandlerSupport,目的是將組件注冊到Spring容器 5、編寫spring.handlers和spring.schemas文件 提供一個簡單的例子,來說明如何去擴展一個Spring配置,需求如下:使用自定義標簽定義一個簡單的bean,這個bean有一個或多個屬性,標簽定義完成后,可以在其他項目中用自定義標簽來定義該bean。 首先,創建一個需要擴展的組件,在這里只是一個簡單的bean,而且這個bean只有一個屬性age. One.java
代碼 - package?com.mysite.tag; ??
- ??
- public?class?One?{ ??
- ????private?String?age; ??
- ???? ??
- ????public?One(){ ??
- ???????? ??
- ????} ??
- ??
- ????public?String?getAge()?{ ??
- ????????return?age; ??
- ????} ??
- ??
- ????public?void?setAge(String?age)?{ ??
- ????????this.age?=?age; ??
- ????} ??
- } ??
然后,建立一個xsd文件,來描述這個bean mytag.xsd
代碼 - <?xml?version="1.0"?encoding="UTF-8"?>??
- <xsd:schema?xmlns="http://www.mysite.org/schema/mytag"??
- ????????xmlns:xsd="http://www.w3.org/2001/XMLSchema"??
- ????????xmlns:beans="http://www.springframework.org/schema/beans"??
- ????????targetNamespace="http://www.mysite.org/schema/mytag"??
- ????????elementFormDefault="qualified"??
- ????????attributeFormDefault="unqualified">??
- ???? ??
- ????<xsd:import?namespace="http://www.springframework.org/schema/beans"/>??
- ???? ??
- ????<xsd:element?name="one">??
- ????????<xsd:complexType>??
- ????????????<xsd:complexContent>??
- ????????????????<xsd:extension?base="beans:identifiedType">??
- ????????????????????<xsd:attribute?name="age"?type="xsd:string"?default="99999"/>??
- ????????????????</xsd:extension>??
- ????????????</xsd:complexContent>??
- ????????</xsd:complexType>??
- ????</xsd:element>??
- </xsd:schema>??
在上面的xsd文件描述了一個新的targetNamespace,并在這個空間中定義了一個name為one的element,one有一個age屬性,類型為string,默認值為99999.xsd文件是xml DTD的替代者,使用XML Schema語言進行編寫,這里對xsd schema不做太多解釋,有興趣可以參考相關的資料。
創建一個Java文件,該文件實現了BeanDefinitionParser接口,用來解析xsd文件中的定義并注冊到組件中。 MyBeanDefinitionParser.java
代碼 - package?com.mysite.tag; ??
- ??
- import?org.springframework.beans.factory.config.BeanDefinition; ??
- import?org.springframework.beans.factory.config.BeanDefinitionHolder; ??
- import?org.springframework.beans.factory.support.BeanDefinitionReaderUtils; ??
- import?org.springframework.beans.factory.support.RootBeanDefinition; ??
- import?org.springframework.beans.factory.xml.BeanDefinitionParser; ??
- import?org.springframework.beans.factory.xml.ParserContext; ??
- import?org.w3c.dom.Element; ??
- ??
- public?class?MyBeanDefinitionParser?implements?BeanDefinitionParser{ ??
- ????public?BeanDefinition?parse(Element?arg0,?ParserContext?arg1)?{ ??
- ????????RootBeanDefinition?def?=?new?RootBeanDefinition(); ??
- ??????????????????
- ????????def.setBeanClass(One.class); ??
- ???????? ??
- ??????????????????
- ????????String?id?=?arg0.getAttribute("id"); ??
- ????????BeanDefinitionHolder?idHolder=?new?BeanDefinitionHolder(def,id); ??
- ????????BeanDefinitionReaderUtils.registerBeanDefinition(idHolder,?arg1.getRegistry()); ??
- ???????? ??
- ??????????????????
- ????????String?age?=?arg0.getAttribute("age"); ??
- ????????BeanDefinitionHolder?ageHolder=?new?BeanDefinitionHolder(def,age); ??
- ????????BeanDefinitionReaderUtils.registerBeanDefinition(ageHolder,?arg1.getRegistry()); ??
- ????????def.getPropertyValues().addPropertyValue("age",?age); ??
- ???????? ??
- ????????return?def; ??
- ????} ??
- } ??
- ??
上面的代碼僅僅實現了一個方法public BeanDefinition parse(Element arg0, ParserContext arg1),設置相關的Bean Class,解析了對應的xsd文件,并將解析的內容注冊到上下文中,同時返回一個BeanDefinition對象(BeanDefinition是Spring的bean定義,提供了bean部分的操作方法,如isSingleton()、isLazyInit()等)。
注意,id屬性是一個默認的屬性,可以不在xsd文件中描述,但是需要注冊它,否則將無法通過getBean方法獲取標簽定義的bean,也無法被其他bean引用。 另外,下面代碼是給bean的屬性賦值,這個是不可缺少的,否則在使用標簽定義時將無法獲取bean屬性的值。
代碼 - def.getPropertyValues().addPropertyValue("age",?age); ??
然后為組件編寫一個Handler文件,擴展自NamespaceHandlerSupport,它的作用是將組件注冊到Spring容器 MyNameSpaceHandler.java
代碼 - package?com.mysite.tag; ??
- ??
- import?org.springframework.beans.factory.xml.NamespaceHandlerSupport; ??
- ??
- public?class?MyNameSpaceHandler?extends?NamespaceHandlerSupport?{ ??
- ??
- ????public?void?init()?{ ??
- ?????????registerBeanDefinitionParser("one",new?MyBeanDefinitionParser()); ??
- ????} ??
- } ??
實際執行的過程只有一句代碼,注冊了一個名字為one的擴展配置,包含了MyBeanDefinitionParser所parser相關xsd的內容。
到了這里,一個Spring擴展標簽已經完成,但是我們目前還沒辦法使用它,Spring沒那么聰明,它無法知道我們在什么地方定義了哪些擴展標簽,這些標簽將被誰解析,這個過程要我們通過一些文件來告知Spring知道,它們就是spring.handlers和spring.schemas,它們放在META-INF目錄中,Spring.jar的META-INF目錄中也有同名的文件,它們的文件內容基本上是相似的,而Spring在執行過程中,如果發現其他jar文件的META-INF文件夾中包含有這兩個文件,Spring將會合并它們。 spring.schemas
代碼 spring.handlers
代碼 spring.schemas將告訴Spring配置文件知道,如果在配置中引用http://www.mysite.org/schema/mytag.xsd,它應該去什么地方找相應的xsd文件。 而spring.handlers文件將告訴Spring,應該使用哪個Handler注冊擴展標簽。 現在為止,一個完整的xml擴展標簽全部完成,做一個小應用測試一下。 將整個項目打包成jar文件(別忘記META-INF內的兩個文件),然后新建一個項目,引用剛才打包的jar文件,并引用Spring相關文件。 需要注意,自定義xml擴展配置只有xsd方式的引用才可以使用. application.xml
代碼 - <?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:tag="http://www.mysite.org/schema/mytag"??
- ????xsi:schemaLocation="http://www.springframework.org/schema/beans ??
- ????http://www.springframework.org/schema/beans/spring-beans-2.0.xsd ??
- ????http://www.mysite.org/schema/mytag ??
- ????http://www.mysite.org/schema/mytag.xsd">??
- ??
- ????<tag:one?id="oneBean"?age="99"/>??
- </beans>??
在xml文件引用上可以看到,配置了一個名字為tag的名稱空間,目標為http://www.mysite.org/schema/mytag命名空間,這個目標名稱空間必須是存在于項目的引用中的(mytag.xsd中所定義的).
代碼 - <tag:one?id="oneBean"?age="99"/> ??
上面定義了一個id為oneBean的組件,使用了“one”擴展標簽,也就是我們在handler中所注冊的,組件age屬性的值為99。
Main.java
代碼 - package?com.test; ??
- ??
- import?org.springframework.context.ApplicationContext; ??
- import?org.springframework.context.support.ClassPathXmlApplicationContext; ??
- ??
- import?com.mysite.tag.One; ??
- ??
- public?class?Main?{ ??
- ??
- ????public?static?void?main(String[]?args)?{ ??
- ????????ApplicationContext?ctx?=?new?ClassPathXmlApplicationContext("application.xml"); ??
- ????????One?tag?=?(One)?ctx.getBean("oneBean"); ??
- ????????System.out.println("oneBean?age?is?"+tag.getAge()); ??
- ????} ??
- } ??
運行測試程序,結果如下
代碼 Spring的xml擴展是一個非常有用的特性,在Spring2.0的版本中也提供了一些新的標簽使用,如<aop>,<tx>等,但就目前來講受大家關注程度并不高,我想大部分使用Spring的開發人員都在使用Spring開發企業應用,而Spring所提供的<bean />定義的方式對于開發人員來說已經能夠滿足需要,但是如果看的更遠一些,在Spring以后的發展過程中,xml擴展應該會成為spring的核心特性之一,或許到時大家會習慣這樣的方式來編寫Spring配置文件吧
代碼 - <XXCompany:XXXModule?id=""> ??
- ... ??
- ... ??
|