Spring是一個(gè)強(qiáng)大的Java應(yīng)用框架,它廣泛地應(yīng)用于Java應(yīng)用程序中,為Plain Old Java Objects(POJO)提供企業(yè)級(jí)服務(wù)。Spring利用依賴注入機(jī)制來簡化工作,同時(shí)提高可測試性。其配置文件(通常是XML格式)中指定了Spring bean、依賴性以及bean所需的服務(wù)。但是,這些XML配置文件既冗長又不實(shí)用。對(duì)于需要定義大量Spring bean的大型項(xiàng)目來說,它們難以閱讀和管理。
在本文中,我將向您展示12種用于Spring XML配置的最佳實(shí)踐。其中的一些實(shí)踐與其說是最佳實(shí)踐,倒不如說是必要實(shí)踐。注意,其他因素(如域模型的設(shè)置)也可能影響XML的配置,但是本文重點(diǎn)研究XML配置的可讀性和可管理性。

1。避免使用自動(dòng)綁定(autowiring)功能
Spring可以通過bean類的自省自動(dòng)綁定依賴性,所以不必顯式指明bean的屬性和構(gòu)造函數(shù)。Bean屬性可以通過屬性名稱或類型匹配來實(shí)現(xiàn)自動(dòng)綁定。構(gòu)造函數(shù)通過類型匹配來實(shí)現(xiàn)自動(dòng)綁定。甚至可以指定自動(dòng)檢測autowiring模式,它可以引導(dǎo)Spring選擇一種適當(dāng)?shù)倪\(yùn)行機(jī)制。先來看看下面的一個(gè)例子:
    <bean id="orderService"
        class="com.lizjason.spring.OrderService"
        autowire="byName"/>
OrderService類的屬性名在容器中用于匹配bean實(shí)例。自動(dòng)綁定可能會(huì)節(jié)省一些鍵入工作量并減少混亂。但是在現(xiàn)實(shí)項(xiàng)目中不應(yīng)該使用這種方式,因?yàn)樗鼱奚伺渲玫目勺x性和可維護(hù)性。許多指南和介紹中大肆吹捧自動(dòng)綁定是Spring的一項(xiàng)極好的特性,而沒有提到這一特性所帶來的犧牲。依我來看,這就像Spring中的對(duì)象池(object-pooling),更大程度上只是宣傳的噱頭。對(duì)于精簡XML配置文件來說,它是一個(gè)好辦法,但它實(shí)際上增加了復(fù)雜性,尤其是在運(yùn)行包含大量類聲明的項(xiàng)目時(shí)。雖然Spring允許混合使用自動(dòng)綁定和顯式綁定,但這會(huì)使XML配置更加晦澀難懂。

2.使用命名約定
該原則對(duì)于Java編碼也一樣適用。在項(xiàng)目中使用清晰的、描述性的、一致的命名約定將非常有利于開發(fā)人員理解XML配置。例如,對(duì)于bean ID,可以按照J(rèn)ava類字段名約定來命名它。OrderServiceDAO實(shí)例的bean ID應(yīng)該命名為orderServiceDAO。對(duì)于大型項(xiàng)目,可以在bean ID前面加上包名作為前綴。

3. 使用簡潔形式
簡潔形式避免了冗長,因?yàn)樗鼘傩灾岛鸵脧淖釉刂幸迫雽傩灾小@缦旅娴睦樱?br />     <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <property name="companyName">
            <value>lizjason</value>
        </property>
        <constructor-arg>
            <ref bean="orderDAO">
        </constructor-arg>
    </bean>
可以使用簡潔形式將上述代碼重寫為:
    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <property name="companyName"
            value="lizjason"/>
        <constructor-arg ref="orderDAO"/>
    </bean>

簡潔形式自1.2版本起就可以使用。注意,對(duì)于<ref local="...">,沒有簡潔形式。
簡潔形式不但可以節(jié)約鍵入工作量,而且可以使XML配置文件更清晰。當(dāng)一個(gè)配置文件中定義了大量的類時(shí),它可以顯著提高可讀性。

4. 對(duì)于構(gòu)造函數(shù)參數(shù)匹配,類型比下標(biāo)好
當(dāng)構(gòu)造函數(shù)含有一個(gè)以上同種類型的參數(shù),或者屬性值的標(biāo)簽已經(jīng)被占用時(shí),Spring允許使用從0開始的下標(biāo)來避免混淆。例如:
    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <constructor-arg index="0" value="lizjason"/>
        <constructor-arg index="1" value="100"/>
    </bean>
利用type屬性來編寫會(huì)更好一些,如下:
    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <constructor-arg type="java.lang.String"
            value="lizjason"/>
        <constructor-arg type="int" value="100"/>
    </bean>

使用index可以減少一些代碼,但是與type屬性相比,它更易于出錯(cuò)且難于閱讀。只有在構(gòu)造函數(shù)參數(shù)不明確的時(shí)候,才應(yīng)該使用index。

5. 盡可能重用已定義的bean
Spring提供了一種類似于繼承的機(jī)制來減少配置信息的復(fù)制并簡化XML配置。定義一個(gè)子類,它就可以從父類那里繼承配置信息,而父類實(shí)際上成為子類的一個(gè)模板。這就是大型項(xiàng)目中所謂的重用。只需在父類bean中設(shè)置abstract=true,然后在子bean中指定parent引用。例如:
    <bean id="abstractService" abstract="true"
        class="com.lizjason.spring.AbstractService">
        <property name="companyName"
            value="lizjason"/>
    </bean>

    <bean id="shippingService"
        parent="abstractService"
        class="com.lizjason.spring.ShippingService">
        <property name="shippedBy" value="lizjason"/>
    </bean>

ShippingService類從abstractService類繼承companyName屬性的值——lizjason。如果一個(gè)bean沒有指定類或工廠方法,那么這個(gè)bean便是抽象的。

6. 在導(dǎo)入時(shí),首選通過ApplicationContext來匯編bean定義
像Ant腳本中的導(dǎo)入一樣,Spring的import元素對(duì)于匯編模塊化的bean定義來說是很有用的。例如:
    <beans>
        <import resource="billingServices.xml"/>
        <import resource="shippingServices.xml"/>
        <bean id="orderService"
            class="com.lizjason.spring.OrderService"/>
    <beans>

然而,相對(duì)于使用import在XML配置中進(jìn)行預(yù)匯編,通過ApplicationContext來配置這些bean則顯得更加靈活。使用ApplicationContext的話,XML配置也更易于管理。可以向ApplictionContext構(gòu)造函數(shù)傳遞一組bean定義,如下:
    String[] serviceResources =
        {"orderServices.xml",
        "billingServices.xml",
        "shippingServices.xml"};
    ApplicationContext orderServiceContext = new
        ClassPathXmlApplicationContext(serviceResources);

7. 使用id作為bean標(biāo)識(shí)符
可以指定一個(gè)id或名稱來作為bean標(biāo)識(shí)符。雖然使用id不能提高可讀性,但是它可以利用XML分析程序來對(duì)bean引用進(jìn)行驗(yàn)證。如果由于XML IDREF的約束而不能使用某個(gè)id,那么可以使用名稱來作為bean的標(biāo)識(shí)符。XML IDREF的約束是:id必須以字母(或者XML規(guī)范中定義的標(biāo)點(diǎn)符號(hào))開頭,后面是字母、數(shù)字、連字符、下劃線、冒號(hào)或句點(diǎn)。實(shí)際上,很少會(huì)遇到XML IDREF約束問題。

8. 在開發(fā)階段使用依賴性檢查(dependency-check)
可以在bean定義中為dependency-check屬性設(shè)置一個(gè)非默認(rèn)值,比如simple、objects或all,以便容器進(jìn)行依賴性檢查。當(dāng)需要顯式或通過自動(dòng)綁定設(shè)置bean的全部屬性(或某類屬性)時(shí),依賴性檢查便顯得很有用。
    <bean id="orderService"
        class="com.lizjason.spring.OrderService"
        dependency-check="objects">
        <property name="companyName"
            value="lizjason"/>
        <constructor-arg ref="orderDAO"/>
    </bean>

在這個(gè)例子中,容器確保為orderService bean設(shè)置的屬性不是primitives或collections。也可以為所有的bean設(shè)置默認(rèn)依賴性檢查,但是我們很少這樣做,因?yàn)橛行゜ean屬性根本就不必設(shè)置。

9. 為每個(gè)配置文件添加首部注釋
最好使用描述性的id和名稱來取代XML配置文件中的內(nèi)置注釋。此外,添加一個(gè)配置文件首部也很有用,它可以概述文件中所定義的bean。可以選擇將描述添加到description標(biāo)簽中。例如:
    <beans>
        <description>
            This file defines billing service
            related beans and it depends on
            baseServices.xml,which provides
            service bean templates...
        </description>
        ...
    </beans>
使用description標(biāo)簽的一個(gè)好處是可以輕松地利用工具從標(biāo)簽中選獲取描述內(nèi)容。

10. 對(duì)于更改,團(tuán)隊(duì)成員要積極交流
在重構(gòu)Java代碼時(shí),需要隨時(shí)更新配置文件并通知團(tuán)隊(duì)成員。XML配置文件也是代碼,它們是應(yīng)用程序的至關(guān)重要的部分,但是它們難于閱讀和維護(hù)。大部分情況下,需要同時(shí)閱讀XML配置文件和運(yùn)行中的Java代碼。

11. Setter注入優(yōu)于構(gòu)造函數(shù)注入
Spring提供了3種類型的依賴注入:構(gòu)造函數(shù)注入(constructor injection)、setter注入(setter injection)和方法注入(method injection)。我們一般只用前兩種。
    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <constructor-arg ref="orderDAO"/>
    </bean>

    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <property name="billingDAO"
            ref="billingDAO">
    </bean>

在這個(gè)例子中,orderService類使用的是構(gòu)造函數(shù)注入,而BillingService類使用的是setter注入。構(gòu)造函數(shù)注入可以確保bean不會(huì)在一個(gè)非法狀態(tài)下被創(chuàng)建,但是setter注入更加靈活且更易于管理,尤其是在類包含許多屬性并且其中一些可選的情況下。

12. 不要濫用依賴注入
最后一點(diǎn),Spring ApplicationContext可以為您創(chuàng)建Java對(duì)象,但并不是所有的Java對(duì)象都應(yīng)通過依賴注入來創(chuàng)建。例如,全局對(duì)象不應(yīng)該通過ApplicationContext來創(chuàng)建。Spring是一個(gè)很棒的框架,但是,就可讀性和易管理性而言,如果定義了大量bean,基于XML的配置就可能成為問題。過度使用依賴注入會(huì)使XML配置變得復(fù)雜且臃腫。要知道,借助于功能強(qiáng)大的IDE(如Eclipse和IntelliJ),Java代碼比XML文件更加易讀、易維護(hù)、易管理。

結(jié)束語
XML是通用的Spring配置方式。但如果定義了大量bean,基于XML的配置就會(huì)變得冗長而不實(shí)用。Spring提供了豐富的配置選項(xiàng),恰當(dāng)?shù)乩闷渲械倪x項(xiàng)可以使XML配置更清晰,但是,有些選項(xiàng)(如autowiring)往往會(huì)降低配置文件的可讀性和可維護(hù)性。遵循本文中所描述的最佳實(shí)踐,將有助于您創(chuàng)建出清晰易讀的XML配置文件。