Spring是一個(gè)強有力的java程序框架,其被廣泛應用于java的程序中。它用POJO提供了企業(yè)級服務(wù)。 Spring利用依賴(lài)注入可以獲得簡(jiǎn)單而有效的測試能力。Spring beans,依賴(lài)關(guān)系,以及服務(wù)所需要的bean都將在配置文件中予以描述,配置文件一般采用XML格式。然而XML配置文件冗長(cháng)而不易使用,在你進(jìn)行一 個(gè)使用了大量bean的大項目中它將變得難以閱讀和控制。
在這篇文章中我將給你展示12種的有關(guān)Spring XML配置文件的最佳技巧。它們中的一些具有更多的實(shí)際意義,而不僅是最好的技巧。請注意另外一些因素,例如域模型的設計,會(huì )影響到XML配置,但是這篇文章更關(guān)注于XML配置的可讀性和可操控性。
1.避免使用自動(dòng)裝配
Spring 可以通過(guò)bean類(lèi)的自省來(lái)實(shí)現自動(dòng)裝配依賴(lài),這樣的話(huà)你就不必明確地描述bean的屬性或者構造函數的參數。根據屬性名稱(chēng)活匹配類(lèi)型,bean屬性可以 自動(dòng)進(jìn)行裝配。而構造函數可以根據匹配類(lèi)型自動(dòng)裝配。你甚至可以設置自動(dòng)裝配進(jìn)行自動(dòng)偵測,這樣Spring替你就會(huì )選擇一個(gè)合適的機制。請看下面的例 子:
Spring 可以通過(guò)bean類(lèi)的自省來(lái)實(shí)現自動(dòng)裝配依賴(lài),這樣的話(huà)你就不必明確地描述bean的屬性或者構造函數的參數。根據屬性名稱(chēng)活匹配類(lèi)型,bean屬性可以 自動(dòng)進(jìn)行裝配。而構造函數可以根據匹配類(lèi)型自動(dòng)裝配。你甚至可以設置自動(dòng)裝配進(jìn)行自動(dòng)偵測,這樣Spring替你就會(huì )選擇一個(gè)合適的機制。請看下面的例 子:
<bean id="orderService"
class="com.lizjason.spring.OrderService"
autowire="byName"/>
OrderService 類(lèi)的屬性名被用來(lái)和容器中的一個(gè)bean實(shí)例進(jìn)行匹配。自動(dòng)裝配會(huì )默默的保存一些類(lèi)型信息并降低混亂。然而,由于它會(huì )犧牲掉這種配置的直觀(guān)性和可維護性, 你在實(shí)際的項目中將不會(huì )用到它。許多指南和陳述材料都把它吹捧為Spring的一個(gè)非常cool的特性,而沒(méi)有提到它的這個(gè)缺點(diǎn)。依我之見(jiàn),就像 Spring的對象池一樣,它更多了一些商業(yè)味道。它看起來(lái)好像可以使XML配置文件更精簡(jiǎn)一些,但實(shí)際上卻增加其復雜性,尤其是在你的較大規模的工程中 已經(jīng)定義了很多bean的時(shí)候更是如此。Spring允許你混合使用自動(dòng)和手動(dòng)裝配,但是這種矛盾會(huì )使XML配置更加的令人費解。
2.使用命名規范
和Java 編碼的理念一樣,在項目中始終用清晰的,描述性的,一致的命名規范對開(kāi)發(fā)人員理解XML配置非常有用。拿bean ID舉例來(lái)說(shuō),你可以遵循Java類(lèi)中屬性的命名規范。比如說(shuō),OrderServiceDAO的bean ID應該是orderServiceDAO。對于大項目來(lái)說(shuō),在bean ID前加包名來(lái)作為前綴。
3.使用簡(jiǎn)化格式
簡(jiǎn)化格式有利于減少冗余,因為它把屬性值和引用作為屬性,而不是子元素??聪旅娴睦樱?div style="height:15px;">
<bean id="orderService" class="com.lizjason.spring.OrderService"> <property name="companyName"> <value>lizjason</value> </property> <constructor-arg> <ref bean="orderDAO"> </constructor-arg> </bean>以上程序可以重新以簡(jiǎn)化格式書(shū)寫(xiě)為:
<bean id="orderService" class="com.lizjason.spring.OrderService"> <property name="companyName" value="lizjason"/> <constructor-arg ref="orderDAO"/> </bean>簡(jiǎn)化格式在1.2版本時(shí)已經(jīng)可用了,但請注意不存在<ref local="...">這種簡(jiǎn)化格式不僅可以較少你的代碼輸入量,而且可以使XML配置更加的清晰。當你的配置文件中存在大量的bean定義時(shí),它可以顯著(zhù)地提高可讀性。
4.盡量使用type而不是index去解決構造函數參數的匹配問(wèn)題
當構造函數中有多個(gè)同類(lèi)型的參數時(shí),Spring只允許你使用從0開(kāi)始的index或者value標簽來(lái)解決這個(gè)問(wèn)題。請看下面的例子:
<bean id="billingService" class="com.lizjason.spring.BillingService"> <constructor-arg index="0" value="lizjason"/> <constructor-arg index="1" value="100"/> </bean>最好用type屬性取代上面的做法:
<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屬性可讀性高。你應該僅在構造函數中有參數沖突時(shí)使用index。
5.如可能,盡量復用bean定義
Spring 提供了一種類(lèi)似于繼承的機制來(lái)降低配置信息的重復并使XML配置更加的簡(jiǎn)單。一個(gè)子bean可以從它的父bean繼承配置信息,本質(zhì)上這個(gè)父bean就像 它的子bean的一個(gè)模板。這是一個(gè)在大型項目中必須使用的特性。所有你要做的就是把父bean的abstract屬性置為true,并在子bean中加 以引用。例如:
<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 bean繼承了abstractService bean的屬性companyName的值lizjason。注意,如果你為bean聲名一個(gè)class或工廠(chǎng)方法,這個(gè)bean將會(huì )默認為abstract
6.盡量使用ApplicationContext裝配bean,而不是用import
像Ant腳本中imports一樣,Spring的import 元素對于模塊化bean的裝配非常有用,例如:
<beans> <import resource="billingServices.xml"/> <import resource="shippingServices.xml"/> <bean id="orderService" class="com.lizjason.spring.OrderService"/> <beans>然而,比起在XML中用imports預裝配這些bean,利用ApplicationContext來(lái)配置它們將更加靈活,也可以使XML配置更加的易于管理。你可以像下面這樣傳遞一個(gè)bean定義數組到ApplicationContext的構造函數中:
String[] serviceResources =
{"orderServices.xml",
"billingServices.xml",
"shippingServices.xml"};
ApplicationContext orderServiceContext = new
ClassPathXmlApplicationContext(serviceResources);
7.用id來(lái)標識bean
你可以用id 或名字作為bean的標識。用id可讀性較差,但是它可以影響XML分析器使bean的reference有效。如果id由于XML IDREF約束而無(wú)法使用,你可以用name作為bean的標識。XML IDREF約束是指id必須以字母開(kāi)始(或者是在XML聲名了的一個(gè)標點(diǎn)符號),后面可以是字母,數字,連字符,下劃線(xiàn),冒號或full stops(不知道怎么翻譯好)。在實(shí)際應用中很少會(huì )遇到XML IDREF約束問(wèn)題。
8.在開(kāi)發(fā)階段使用依賴(lài)檢查
你可以為bean的dependency-check屬性設置一個(gè)值來(lái)取代默認的none,比如說(shuō)simple,objects或者all,這樣的話(huà)容器將替你做依賴(lài)有效性的檢查。當一個(gè)bean的所有屬性(或者某些屬性目錄)都被明確設置,或利用自動(dòng)裝配時(shí)將會(huì )非常有用。
<bean id="orderService" class="com.lizjason.spring.OrderService" dependency-check="objects"> <property name="companyName" value="lizjason"/> <constructor-arg ref="orderDAO"/> </bean>在這個(gè)例子中,容器將確保這些屬性不是privitives或者保證collections是為orderService bean設置的。為所有的bean設置默認的依賴(lài)檢查是可能的,但這個(gè)特性由于有些bean的屬性不需要設置而很少使用。
9.為每個(gè)配置文件加一個(gè)描述注釋
在XML配置文件中最好使用有描述性的id和name,而不是成堆的注釋。另外,加一個(gè)文件描述頭將會(huì )非常有用,這個(gè)描述可以概括文件中定義的bean。另一個(gè)選擇,你可以在description元素中加入描述信息。例如:
<beans> <description> This file defines billing service related beans and it depends on baseServices.xml,which provides service bean templates... </description> ... </beans>用description元素的一個(gè)好處就是工具可以很容易的把描述信息從這個(gè)元素中提取出來(lái)。
10. 和team members溝通變更
當你修改java源碼后,要確保更改了配置文件中的相應部分并把這個(gè)情況告知你的team members。XML配置文件也是代碼,它們是程序的重要組成部分,但它們很難閱讀和維護。大多數時(shí)間里,你需要同時(shí)看XML配置文件和java代碼才能知道是怎么回事。
11. setter注入和構造函數注入,優(yōu)先使用前者
Spring提供了三種注入方式:構造函數注入,setter注入和方法注入。一般我們使用前兩種。
<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 bean用了構造函數注入,而B(niǎo)illingService bean用了setter注入。構造函數注入可以確保bean正確地構建,但是setter注入更加的靈活和易于控制,特別是當class有多個(gè)屬性并且它們中的一些是可選的情況是更是如此。
12. 不要濫用注入
就像前面提到的,Spring的ApplicationContext
Eclipse and
IntelliJ,java代碼更加的易于閱讀,維護和管理比使XML文件可以替你創(chuàng )建java對象,但不是所有的java對象都應該通過(guò)注入創(chuàng )建。例如,
如何使用spring的作用域:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
這里的scope就是用來(lái)配置spring bean的作用域,它標識bean的作用域。
在spring2.0之前bean只有2種作用域即:singleton(單例)、non-singleton(也稱(chēng) prototype), Spring2.0以后,增加了session、request、global session三種專(zhuān)用于Web應用程序上下文的Bean。因此,默認情況下Spring2.0現在有五種類(lèi)型的Bean。當然,Spring2.0對 Bean的類(lèi)型的設計進(jìn)行了重構,并設計出靈活的Bean類(lèi)型支持,理論上可以有無(wú)數多種類(lèi)型的Bean,用戶(hù)可以根據自己的需要,增加新的Bean類(lèi) 型,滿(mǎn)足實(shí)際應用需求。
1、singleton作用域
當一個(gè)bean的作用域設置為singleton, 那么Spring IOC容器中只會(huì )存在一個(gè)共享的bean實(shí)例,并且所有對bean的請求,只要id與該bean定義相匹配,則只會(huì )返回bean的同一實(shí)例。換言之,當把 一個(gè)bean定義設置為singleton作用域時(shí),Spring IOC容器只會(huì )創(chuàng )建該bean定義的唯一實(shí)例。這個(gè)單一實(shí)例會(huì )被存儲到單例緩存(singleton cache)中,并且所有針對該bean的后續請求和引用都 將返回被緩存的對象實(shí)例,這里要注意的是singleton作用域和GOF設計模式中的單例是完全不同的,單例設計模式表示一個(gè)ClassLoader中 只有一個(gè)class存在,而這里的singleton則表示一個(gè)容器對應一個(gè)bean,也就是說(shuō)當一個(gè)bean被標識為singleton時(shí) 候,spring的IOC容器中只會(huì )存在一個(gè)該bean。
配置實(shí)例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
或者
<bean id="role" class="spring.chapter2.maryGame.Role" singleton="true"/>
2、prototype
prototype作用域部署的bean,每一次請求(將其注入到另一個(gè)bean中,或者以程序的方式調用容器的getBean()方法)都會(huì )產(chǎn)生一個(gè)新的bean實(shí)例,相當與一個(gè)new的操作,對于prototype作用域的bean,有一點(diǎn)非常重要,那就是Spring不能對一個(gè)prototype bean的整個(gè)生命周期負責,容器在初始化、配置、裝飾或者是裝配完一個(gè)prototype實(shí)例后,將它交給客戶(hù)端,隨后就對該prototype實(shí)例不聞不問(wèn)了。不管何種作用域,容器都會(huì )調用所有對象的初始化生命周期回調方法,而對prototype而言,任何配置好的析構生命周期回調方法都將不會(huì )被調用。 清除prototype作用域的對象并釋放任何prototype bean所持有的昂貴資源,都是客戶(hù)端代碼的職責。(讓Spring容器釋放被singleton作用域bean占用資源的一種可行方式是,通過(guò)使用 bean的后置處理器,該處理器持有要被清除的bean的引用。)
配置實(shí)例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="prototype"/>
或者
<beanid="role" class="spring.chapter2.maryGame.Role" singleton="false"/>
3、request
request表示該針對每一次HTTP請求都會(huì )產(chǎn)生一個(gè)新的bean,同時(shí)該bean僅在當前HTTP request內有效,配置實(shí)例:
request、session、global session使用的時(shí)候首先要在初始化web的web.xml中做如下配置:
如果你使用的是Servlet 2.4及以上的web容器,那么你僅需要在web應用的XML聲明文件web.xml中增加下述ContextListener即可:
<web-app>
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
</web-app>
,如果是Servlet2.4以前的web容器,那么你要使用一個(gè)javax.servlet.Filter的實(shí)現:
<web-app>
..
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
接著(zhù)既可以配置bean的作用域了:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="request"/>
4、session
session作用域表示該針對每一次HTTP請求都會(huì )產(chǎn)生一個(gè)新的bean,同時(shí)該bean僅在當前HTTP session內有效,配置實(shí)例:
配置實(shí)例:
和request配置實(shí)例的前提一樣,配置好web啟動(dòng)文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="session"/>
5、global session
global session作用域類(lèi)似于標準的HTTP Session作用域,不過(guò)它僅僅在基于portlet的web應用中才有意義。Portlet規范定義了全局Session的概念,它被所有構成某個(gè) portlet web應用的各種不同的portlet所共享。在global session作用域中定義的bean被限定于全局portlet Session的生命周期范圍內。如果你在web中使用global session作用域來(lái)標識bean,那么web會(huì )自動(dòng)當成session類(lèi)型來(lái)使用。
配置實(shí)例:
和request配置實(shí)例的前提一樣,配置好web啟動(dòng)文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/>
6、自定義bean裝配作用域
在spring2.0中作用域是可以任意擴展的,你可以自定義作用域,甚至你也可以重新定義已有的作用域(但是你不能覆蓋singleton和 prototype),spring的作用域由接口org.springframework.beans.factory.config.Scope來(lái)定 義,自定義自己的作用域只要實(shí)現該接口即可,下面給個(gè)實(shí)例:
我們建立一個(gè)線(xiàn)程的scope,該scope在表示一個(gè)線(xiàn)程中有效,代碼如下:
publicclass MyScope implements Scope {
privatefinal ThreadLocal threadScope =new ThreadLocal() {
protected Object initialValue() {
returnnew HashMap();
}
};
public Object get(String name, ObjectFactory objectFactory) {
Map scope = (Map) threadScope.get();
Object object = scope.get(name);
if(object==null) {
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
public Object remove(String name) {
Map scope = (Map) threadScope.get();
return scope.remove(name);
}
publicvoid registerDestructionCallback(String name, Runnable callback) {
}
public String getConversationId() {
// TODO Auto-generated method stub
returnnull;
}
}
關(guān)于spring中<util:**/>的配置探索<util/>命名空間
事情的發(fā)展總是一段曲折前進(jìn)的過(guò)程。當Spring剛出現時(shí),開(kāi)發(fā)者可以使用<list/>、<map/>、<set/>等元素定義集合,然而這些集合不能夠在不同的受管Bean間進(jìn)行復用。盡管開(kāi)發(fā)者可以采用抽象Bean機制實(shí)現復用,但實(shí)在不怎么優(yōu)雅。與此同時(shí),開(kāi)發(fā)者借助ListFactoryBean、MapFactoryBean和SetFactoryBean等對象能夠定義出可供復用的集合。然而,這也不是很友好的做法。再后來(lái),<util/>命名空間被Spring 2.x引入,這才使得集合的定義變得簡(jiǎn)單。
首先在spring的配置文件中添加
Xml代碼
<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:util=<a >http://www.springframework.org/schema/util</a>
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
<a >
</a>
Xml代碼
<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:util=<a >http://www.springframework.org/schema/util</a>
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
<a >
</a>
1. <util:constant/>元素
比如某類(lèi)存在如下字段定義
Java代碼
public static final String hwStatic = "hello static constant";
Java代碼
public static final String hwStatic = "hello static constant";
如果希望以上屬性取值作為受管Bean,可以如下配置:
Xml代碼
<util:constant id="hwConstant" static-field="test.HelloWorld.hwStatic"/>
Xml代碼
<util:constant id="hwConstant" static-field="test.HelloWorld.hwStatic"/>
這樣就將java代碼中的常量hwStatic(在test包下的HelloWorld類(lèi)中)配置給spring進(jìn)行管理,id為另起的名字;
又eg:
Xml代碼
<util:constant id="maxValue" static-field="java.lang.Integer.MAX_VALUE"/>
Xml代碼
<util:constant id="maxValue" static-field="java.lang.Integer.MAX_VALUE"/>
2. <util:property-path/>元素
Xml代碼
id="property-path" path="helloWorld.hello"/>
<bean id="helloWorld" class="test.HelloWorld">
<property name="hello" value="hi"/>
</bean>
Xml代碼
id="property-path" path="helloWorld.hello"/>
<bean id="helloWorld" class="test.HelloWorld">
<property name="hello" value="hi"/>
</bean>
這里path="helloworld.hello"就是指bean為"helloworld"的屬性hello。
3. <util:properties/>元素
"classpath:"表明,將從類(lèi)路徑上查找并裝載xxx屬性文件.
Xml代碼
<util:properties id="xxx" location="classpath:xxxxx.properties">
Xml代碼
<util:properties id="xxx" location="classpath:xxxxx.properties">
4. <util:list/>元素
Xml代碼
<util:list id="listUtil" list-class="java.util.ArrayList">
<value>first</valuse>
<value>two</valuse>
<value>three</valuse>
<value>ten</valuse>
</util:list>
Xml代碼
<util:list id="listUtil" list-class="java.util.ArrayList">
<value>first</valuse>
<value>two</valuse>
<value>three</valuse>
<value>ten</valuse>
</util:list>
它的作用就是在spring啟動(dòng)初始化bean時(shí),給listUtil這個(gè)list賦值為這四個(gè)值。 下面的同理
5. <util:map/>元素
Xml代碼
<bean id="abstractCollectionBean" abstract="true">
<property name="map">
<map>
<entry key="mapKey1" value="mapValue1">
<entry key="mapKey2" value="mapValue2">
</map>
</property>
</bean>
Xml代碼
<bean id="abstractCollectionBean" abstract="true">
<property name="map">
<map>
<entry key="mapKey1" value="mapValue1">
<entry key="mapKey2" value="mapValue2">
</map>
</property>
</bean>
繼承了abstractCollectionBean的子bean
Xml代碼
<bean id="CollectionBean" class="test.CollectionBean" parent="abstractCollectionBean">
<property name="map">
<map merge="true" key-type="java.lang.String" value-type="java.lang.String">
<entry key="mapKey1" value="mapValue1Override"/>
<entry>
<key><value>mapKey2</value></key>
<value>mapValue2</value>
</entry>
<entry key="testBean" value-ref="testBean">
</map>
</property>
</bean>
<bean id="testBean" class="test.TestBean" />
Xml代碼
<bean id="CollectionBean" class="test.CollectionBean" parent="abstractCollectionBean">
<property name="map">
<map merge="true" key-type="java.lang.String" value-type="java.lang.String">
<entry key="mapKey1" value="mapValue1Override"/>
<entry>
<key><value>mapKey2</value></key>
<value>mapValue2</value>
</entry>
<entry key="testBean" value-ref="testBean">
</map>
</property>
</bean>
<bean id="testBean" class="test.TestBean" />
為了簡(jiǎn)化MapFactoryBean對象的使用,可使用如下代碼 :
Xml代碼
<util:map id="mapUtil" map-class="java.util.HashMap">
<entry key="1" value="first">
<entry key="2" value="two">
<entry key="3" value="three">
</util:map>
Xml代碼
<util:map id="mapUtil" map-class="java.util.HashMap">
<entry key="1" value="first">
<entry key="2" value="two">
<entry key="3" value="three">
</util:map>
6. <util:set/>元素
同樣的,為了簡(jiǎn)化SetFactoryBean對象,可使用如下代碼 :
Xml代碼
<util:set id="setUtil" set-class="java.util.HashSet">
<value>first</value>
<value>two</value>
<value>three</value>
</util:set>
Xml代碼
<util:set id="setUtil" set-class="java.util.HashSet">
<value>first</value>
<value>two</value>
<value>three</value>
</util:set>
7. 使用<p/>命名空間
在xml頭加入 xmlns:p=
http://www.springframework.org/schema/p;這里的p就是property的意思。
例如如下代碼:
Xml代碼
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" ref="locations"/>
<property name="order" value="1"/>
</bean>
<util:list id="locations">
<value>userinfo.properties</value>
</util:list>
Xml代碼
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" ref="locations"/>
<property name="order" value="1"/>
</bean>
<util:list id="locations">
<value>userinfo.properties</value>
</util:list>
在導入了</p>命名空間后,等價(jià)于
Xml代碼
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:locations-ref="locations" p:order="1" />
<util:list id="locations">
<value>userinfo.properties</value>
</util:list>
Xml代碼
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:locations-ref="locations" p:order="1" />
<util:list id="locations">
<value>userinfo.properties</value>
</util:list>
實(shí)例:
http://blog.csdn.net/daryl715/archive/2007/09/26/1802292.aspx原創(chuàng )地址:
http://wutheringsea.iteye.com/blog/647924