Spring 2.0版本支持?jǐn)U展XML配置,著實(shí)興奮了一下,在我看來,Spring作為目前最流行的框架,不能擴(kuò)展用戶自定義的配置,實(shí)在是Spring的一個(gè)很不爽的地方,<bean />的方式用起來比較通用,起碼到目前為止符合大部分人的使用習(xí)慣,并且能完成Spring所有的配置操作,但是對(duì)于第三方的提供商或則會(huì)經(jīng)常擴(kuò)展Spring功能的開發(fā)者來說,使用<bean />這樣的配置方式或許不是他們最想要的,他們需要使組件的配置更加直觀、易閱讀、易擴(kuò)展……試想使用下面的配置方式
代碼
-
<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"
??
-
???????/> ??
上面的代碼中配置了兩個(gè)組件,datasource和cache組件,相比普通的bean&propertiy方式,很顯然,這種配置方式更直觀,更易讀,更重要的是,如果作為組件發(fā)布,使用者也可以很方便快捷的開始使用,而不需要關(guān)心組件的任何實(shí)現(xiàn)細(xì)節(jié)。
擴(kuò)展XML配置大致需要一下幾個(gè)步驟 1、創(chuàng)建一個(gè)需要擴(kuò)展的組件 2、定義一個(gè)xsd文件描述組件內(nèi)容 3、創(chuàng)建一個(gè)文件,實(shí)現(xiàn)BeanDefinitionParser接口,用來解析xsd文件中的定義和組件定義 4、創(chuàng)建一個(gè)Handler文件,擴(kuò)展自NamespaceHandlerSupport,目的是將組件注冊(cè)到Spring容器 5、編寫spring.handlers和spring.schemas文件 提供一個(gè)簡(jiǎn)單的例子,來說明如何去擴(kuò)展一個(gè)Spring配置,需求如下:使用自定義標(biāo)簽定義一個(gè)簡(jiǎn)單的bean,這個(gè)bean有一個(gè)或多個(gè)屬性,標(biāo)簽定義完成后,可以在其他項(xiàng)目中用自定義標(biāo)簽來定義該bean。 首先,創(chuàng)建一個(gè)需要擴(kuò)展的組件,在這里只是一個(gè)簡(jiǎn)單的bean,而且這個(gè)bean只有一個(gè)屬性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; ??
- ????} ??
- } ??
然后,建立一個(gè)xsd文件,來描述這個(gè)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文件描述了一個(gè)新的targetNamespace,并在這個(gè)空間中定義了一個(gè)name為one的element,one有一個(gè)age屬性,類型為string,默認(rèn)值為99999.xsd文件是xml DTD的替代者,使用XML Schema語言進(jìn)行編寫,這里對(duì)xsd schema不做太多解釋,有興趣可以參考相關(guān)的資料。
創(chuàng)建一個(gè)Java文件,該文件實(shí)現(xiàn)了BeanDefinitionParser接口,用來解析xsd文件中的定義并注冊(cè)到組件中。 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; ??
- ????} ??
- } ??
- ??
上面的代碼僅僅實(shí)現(xiàn)了一個(gè)方法public BeanDefinition parse(Element arg0, ParserContext arg1),設(shè)置相關(guān)的Bean Class,解析了對(duì)應(yīng)的xsd文件,并將解析的內(nèi)容注冊(cè)到上下文中,同時(shí)返回一個(gè)BeanDefinition對(duì)象(BeanDefinition是Spring的bean定義,提供了bean部分的操作方法,如isSingleton()、isLazyInit()等)。
注意,id屬性是一個(gè)默認(rèn)的屬性,可以不在xsd文件中描述,但是需要注冊(cè)它,否則將無法通過getBean方法獲取標(biāo)簽定義的bean,也無法被其他bean引用。 另外,下面代碼是給bean的屬性賦值,這個(gè)是不可缺少的,否則在使用標(biāo)簽定義時(shí)將無法獲取bean屬性的值。
代碼 - def.getPropertyValues().addPropertyValue("age",?age); ??
然后為組件編寫一個(gè)Handler文件,擴(kuò)展自NamespaceHandlerSupport,它的作用是將組件注冊(cè)到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()); ??
- ????} ??
- } ??
實(shí)際執(zhí)行的過程只有一句代碼,注冊(cè)了一個(gè)名字為one的擴(kuò)展配置,包含了MyBeanDefinitionParser所parser相關(guān)xsd的內(nèi)容。
到了這里,一個(gè)Spring擴(kuò)展標(biāo)簽已經(jīng)完成,但是我們目前還沒辦法使用它,Spring沒那么聰明,它無法知道我們?cè)谑裁吹胤蕉x了哪些擴(kuò)展標(biāo)簽,這些標(biāo)簽將被誰解析,這個(gè)過程要我們通過一些文件來告知Spring知道,它們就是spring.handlers和spring.schemas,它們放在META-INF目錄中,Spring.jar的META-INF目錄中也有同名的文件,它們的文件內(nèi)容基本上是相似的,而Spring在執(zhí)行過程中,如果發(fā)現(xiàn)其他jar文件的META-INF文件夾中包含有這兩個(gè)文件,Spring將會(huì)合并它們。 spring.schemas
代碼 spring.handlers
代碼 spring.schemas將告訴Spring配置文件知道,如果在配置中引用http://www.mysite.org/schema/mytag.xsd,它應(yīng)該去什么地方找相應(yīng)的xsd文件。 而spring.handlers文件將告訴Spring,應(yīng)該使用哪個(gè)Handler注冊(cè)擴(kuò)展標(biāo)簽。 現(xiàn)在為止,一個(gè)完整的xml擴(kuò)展標(biāo)簽全部完成,做一個(gè)小應(yīng)用測(cè)試一下。 將整個(gè)項(xiàng)目打包成jar文件(別忘記META-INF內(nèi)的兩個(gè)文件),然后新建一個(gè)項(xiàng)目,引用剛才打包的jar文件,并引用Spring相關(guān)文件。 需要注意,自定義xml擴(kuò)展配置只有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文件引用上可以看到,配置了一個(gè)名字為tag的名稱空間,目標(biāo)為http://www.mysite.org/schema/mytag命名空間,這個(gè)目標(biāo)名稱空間必須是存在于項(xiàng)目的引用中的(mytag.xsd中所定義的).
代碼 - <tag:one?id="oneBean"?age="99"/> ??
上面定義了一個(gè)id為oneBean的組件,使用了“one”擴(kuò)展標(biāo)簽,也就是我們?cè)趆andler中所注冊(cè)的,組件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()); ??
- ????} ??
- } ??
運(yùn)行測(cè)試程序,結(jié)果如下
代碼 Spring的xml擴(kuò)展是一個(gè)非常有用的特性,在Spring2.0的版本中也提供了一些新的標(biāo)簽使用,如<aop>,<tx>等,但就目前來講受大家關(guān)注程度并不高,我想大部分使用Spring的開發(fā)人員都在使用Spring開發(fā)企業(yè)應(yīng)用,而Spring所提供的<bean />定義的方式對(duì)于開發(fā)人員來說已經(jīng)能夠滿足需要,但是如果看的更遠(yuǎn)一些,在Spring以后的發(fā)展過程中,xml擴(kuò)展應(yīng)該會(huì)成為spring的核心特性之一,或許到時(shí)大家會(huì)習(xí)慣這樣的方式來編寫Spring配置文件吧
代碼 - <XXCompany:XXXModule?id=""> ??
- ... ??
- ... ??
|