時間:2006-06-08 作者:Dejan Bosanac 瀏覽次數: 701 本文關鍵字:Spring,?configuration,?source control,?CVS,?subversion,?配置,?源代碼控制 |
|

本文將為您提供關于Spring MVC框架的配置技巧,以幫助管理基于Spring的web應用程序的多個實例。本配置管理主題常被學術界所忽略,但是,這對于現實的web開發尤為重要。本主題并不直接關聯任何具體的技術,因此,我們將從最基本的概念開始對這個問題進行說明。下面,我們將根據Spring MVC框架,為基于本技術開發的項目提供一系列的解決方案。
Spring配置
人們經常會在一臺以上的主機上配置一種Web應用程序。例如,在生產中,一個網站可能只有一個實例。除了此實例外,開發人員可以在用于開發的機器上配置其他的(開發)實例。也可以在公司(機構)內部的本地開發服務器上維護其他應用程序裝置,這將讓您受益匪淺。該實例的目的是使Web設計者可以獲得有質量保證的材料,并為需要為應用程序提供文件資料的人提供準入。
大家都知道,即使是最簡單的場景,也需要安裝、配置和維護三個實例。而對于位于不同地理位置的團隊來說,要從事這樣的項目便更加困難。對于任何不是特別簡單的Web應用程序項目,都需要多名開發人員來安裝項目裝置和本地設置以及運行單元測試的裝置等。
很多組織都將自己開發的產品作為Web應用程序。我們可以在很多產品中發現這種情況,例如電子商務系統、內容管理系統(CMS),以及博客發布平臺等。這類產品可在多個服務器中進行部署。對于成功的多用途Web應用程序來說,他們的開發人員必須要保證他們的應用程序便于安裝,并且能夠與其他Web應用程序完美集成。經過上述討論之后,我們應該明了,作為本文主題的應用程序配置是通用Web應用程序項目開發人員所需要解決的重要問題之一。
諸如CVS或Subversion之類的版本控制系統是開發組織使用的一種標準工具。這種工具代表了一些組織的中心源代碼版本庫,它們被用于保持源代碼的有序。用戶可以跟蹤應用程序源代碼的變化,顯示不同版本的區別,并可以確定項目分支。而且,它們使得在應用程序部署中進行部分更新成為可能。
很明顯,版本控制系統軟件是跟蹤源代碼所必需的,它對于解決應用程序配置問題有非常大的幫助。在本文中,我們將不會把重點放在版本控制系統上,因為這方面已經有很多相關的材料了。在此,我們將關注版本控制問題中的一個小話題:如何使Web應用程序的配置更加便捷(尤其是使用Spring MVC框架編寫的Web應用程序)。
問題是:我們在此討論的是一種什么樣的配置?任何Web應用程序都需要一些資源,這些資源通常都是其所運行的服務器所特有的,例如數據庫URL、發送電子郵件的SMTP服務器,以及包含專用軟件文件的文件夾等。這樣的設置應該集中,從而使應用程序配置更加簡單。
但是,這只是這個問題最簡單的一種版本。有時候,在應用程序開發中需要更加復雜的配置。這意味著,必須將各次部署中的不同Bean連接起來,而這會使問題更加復雜。
這些應用程序配置問題的解決方案有諸多優勢,包括:簡化應用程序的安裝和配置過程,使源代碼版本控制更加簡便,減少源代碼版本庫中的沖突現象。下面,我們將通過示例詳細討論這個話題。
問題
我們首先來演示一下上文所提到的最簡單的版本。在這一場景中,我們希望在應用程序部署中改變的是簡單的配置參數,例如鏈接、密碼等。如果您曾經使用Spring MVC框架開發過Web應用程序,那么您應該知道這里將用到的兩個配置文件:
- /WEB-INF/applicationContext.xml,它讓您可以配置Bean,或者顯示應用程序上下文。通過這個文件,您可以定義自己的業務邏輯Bean、資源以及其他可以與Web端口相關聯的所有Bean。
- /WEB-INF/[servlet-name]-servlet.xml,它被用于配置Web層、視圖解析器、控制器、校驗器以及其他所有MVC框架中必需的Bean。[servlet-name]指的是在web.xml部署描述符中定義的Spring dispatcher servlet的名稱。
那么問題在哪兒呢?問題就出在applicationContext.xml中將包括一些特定于主機的Bean定義。其中,最明顯的一個示例就是包含了JDBC連接信息的bean,但是任何一種稍微復雜些的應用程序都有十幾個類似的Bean。看一下下面的示例:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.postgresql.Driver</value> </property> <property name="url"> <value>jdbc:postgresql://localhost/test</value> </property> <property name="username"> <value>postgres</value> </property> <property name="password"> <value></value> </property> </bean>
這個解決方案的問題在于對applicationContext.xml文件的維護。對于初學者來說,設想一下,項目放在源代碼版本控制系統中,例如CVS。下面,假設您希望在網站中添加新的功能,那么就需要在應用程序上下文定義中添加額外的Bean定義。問題是如何在生產服務器上體現這些改變。
通常情況下,應用程序的本地實例不會與活動站點使用同樣的數據庫,因此applicationContext.xml文件將包括讓您能夠訪問本地數據庫的設置。當您想提交在源代碼版本庫中的改變時,就需要注意這些特定于主機屬性的同步性。版本庫中的文件最終可能使用本地設置中的配置。如果想在生產服務器上更新配置,就必須手動同步這些屬性的值。這是非常枯燥的任務,而且還非常容易出錯。
對于應用程序的每個實例來說,這個問題更加重要。假如有三位開發人員正在使用代碼段基址,而且他們使用的是本地的數據庫。當您提交更改的時候,他們每個人在本地服務器上更新源代碼的時候都必須非常謹慎。他們會手動同步這些更改,然后提交他們的工作。這樣一來,版本控制系統對于這些配置文件來說已經毫無用處。如果曾經使用過Spring MVC,那么您應該知道applicationContext.xml是應用程序中的關鍵組件,因為是它將所有的東西粘合在一起。所以,我們需要一種機制來幫助使應用程序中各項保持有序,這點非常重要。
正如前面所提到的,這是您可能遇到的較簡單的配置問題。更難的問題出現在當需要在不同服務器中進行不同的Bean連接的時候。這類問題常會出現在日常軟件開發任務中。例如,假如您的產品有一個客戶身份驗證模塊,可以對來自關系數據庫或LDAP服務器中的用戶進行身份驗證。自然,這一身份驗證模塊可以使用抽象了特定版本庫的Bean進行配置。如果您想改變不同應用程序部署中驗證用戶的方式,就需要在applicationContext.xml文件中進行不同的Bean連接。這種配置問題常見于在部署中有可配置特性的所有應用程序。
在下文中,我們將討論這兩種配置問題。首先我們會關注同步的Bean屬性問題及其解決方案,接下來,我們會討論更加復雜的同步Bean連接問題。
解決方案
同步Bean屬性
這個問題的一種可行的解決方案是將所有特定于主機的參數都放到普通的Java屬性文件中,使用Spring的PropertyPlaceHolderConfigurer類,將這些參數寫入Bean屬性中。
使用這一解決方案,我們可以生成如下的屬性文件(/WEB-INF/jdbc.properties):
jdbc.driver=org.postgresql.Driver jdbc.url=jdbc:postgresql://localhost/test jdbc.user=postgres jdbc.password=
我們的Bean配置如下:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"> <value>/WEB-INF/jdbc.properties</value> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${jdbc.driver}</value> </property> <property name="url"> <value>${jdbc.url}</value> </property> <property name="username"> <value>${jdbc.user}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property> </bean>
如上所述,我們定義了一個PropertyPlaceholderConfigurer類的實例,并將其位置屬性設置為我們的屬性文件。該類被實現為Bean工廠的后處理器,并將使用定義在文件中的屬性來代替所有的占位符(${...}value)。
利用這種技術,我們可以從applicationContext.xml中移除所有特定于主機的配置屬性。通過這種方式,我們可以自由地為該文件添加新的Bean,而不必擔心特定于主機屬性的同步性。這樣可以簡化生產部署和維護。
同步性連接
上面的技術解決了第一個問題,可是如果您計劃修改不同應用程序部署之間的Bean連接,這一技術便不很適合。針對這一問題的一個解決方案便是額外創建一個名為applicationContext-[hostname].xml 的XML定義文件。其中[hostname]是部署應用程序的主機的名稱。例如,在本地的機器上,這個文件通常名為applicationContext-localhost.xml,而在部署時,它可能更名為applicationContext-somehost.com.xml。
可以猜測,這一文件必須包括特定于某一主機的所有配置Bean。在本文中,我們將假設dataSource bean定義將位于這類文件中,而不是通用的applicationContext.xml定義。當然,這種機制與前者并非沖突,但是為了更加簡單明了,我們將只關注這種方法。
既然我們已經有了特定的配置,下面我們就來討論一下如何將其整合到整個Spring MVC配置概念中。要達到這一目的,可以有許多方法,我們將詳細地一一說明。但首先,我們應該注意到,由于有些Bean可能位于獨立的配置文件中,因此在applicationContext.xml中,所有對它們的局部引用都必須更換成全局名稱。
例如,如下引用:
<property name="someProperty"> <ref local="someBean"/> </property>
應更改為:
<property name="someProperty"> <ref bean="someBean"/> </property>
在這之后,我們有很多可以添加額外的資源以用于配置的方式。其中最明顯的就是使用<import>標簽將這一額外資源包含在applicationContext.xml配置文件中。使用時,要將該標簽放在applicationContext.xml文件開頭。例如:
<import resource="applicationContext-somehost.com.xml"/>
現在,在獨立的XML定義文件和普通的應用程序上下文定義文件中的所有通用Bean定義都有了特定于主機的連接。由于大多數的Bean都不是特定于主機的,因此我們可以像處理Web應用程序中的其他資源一樣自由地處理applicationContext.xml文件,并可以通過合適的版本控制系統與其進行同步。
但是,上述方法也有一定的弊端。如果您想保留不同XML文件的不同配置,就仍然必須擔心applicationContext.xml的同步性,因為資源的名稱必須根據不同服務器進行更改。雖然與原有的解決方案相比有了很大提高,只需更改文件名,但是這還是需要開發人員的手動協助。
由于與applicationContext.xml相比,主機配置不需如此頻繁地進行更改,因此下一步便是將主機配置移動到web.xml文件中(如果可能的話)。幸運的是,我們有一個可用的解決方案。看一下下面關于web.xml配置的片斷:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext.xml /WEB-INF/applicationContext-somehost.com.xml </param-value> </context-param>
正如您所看到的,除了web.xml文件中常有的ContextLoaderListener之外,我們還添加了contextConfigLocation上下文參數配置。這一參數用于指示框架查找這些配置文件的位置。如果這一參數被省略,則Spring就只能到applicationContext.xml中查找。這里我們也定義了特定于主機的配置文件來使用。
利用這種方法,我們將所有特定于主機的配置從applicationContext.xml文件中移除,這樣便減輕了其在不同應用程序部署中的同步性。
如果這種方法成為您的新習慣,您還可以使其更加靈活。通過遵守下列指令,也可以將特定于主機的配置從web.xml文件中移除。
為此,需要創建特定于我們的應用程序上下文的類:
package net.nighttale.spring.util; import java.net.InetAddress; import org.springframework.web.context.support.XmlWebApplicationContext; public class PerHostXmlWebApplicationContext extends XmlWebApplicationContext { protected String[] getDefaultConfigLocations() { String hostname = "localhost"; try { hostname = InetAddress.getLocalHost().getHostName(); } catch (Exception e) { } String perHostConfiguration = DEFAULT_CONFIG_LOCATION_PREFIX + "applicationContext-" + hostname + DEFAULT_CONFIG_LOCATION_SUFFIX ; logger.debug( "Adding per host configuration file: " + perHostConfiguration ); if (getNamespace() != null) { return new String[] { DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX , perHostConfiguration}; } else { return new String[] { DEFAULT_CONFIG_LOCATION , perHostConfiguration}; } } }
這個類拓展了Spring中常被作為默認值使用的XmlWebApplicationContext。XmlWebApplicationContext類將Web應用程序的配置從XML定義文件中復制過來。默認情況下,它可以配置來自applicationContext.xml和[servlet-name]-servlet.xml文件中的應用程序。這個類執行的惟一一項額外任務便是獲取它所在的主機名稱,并將applicationContext-[hostname].xml文件添加到配置文件列表中。
為了使用這個類,我們需要對其進行編譯,將其包含在類途徑中,并指示Spring框架使用它。前兩步非常簡單,我們就不在此贅述。我們可以指示Sping通過contextClass上下文參數來使用它。除了web.xml文件中的原有配置,我們還可以添加下列內容:
<context-param> <param-name>contextClass</param-name> <param-value> net.nighttale.spring.util.PerHostXmlWebApplicationContext </param-value> </context-param>
如果我們使用這一配置片斷,將會有三個文件被用于初始化這個框架:[servlet-name]-servlet.xml、applicationContext-[hostname].xml以及applicationContext.xml。
正如您所看到的,applicationContext.xml和web.xml文件已經完全擺脫了任何特定的配置細節,而且您也不必擔心會在更新應用程序時破壞配置。
但是,這種方法有一個不足之處。因為,不論是否會使用,都需要在應用程序部署中有第三個配置文件。在這種情況下,便不需要特定于主機的配置。例如:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans></beans>
最后,需要知道應用程序上下文類需要查找的特定主機名。檢查主機名稱的最簡單的方法是在機器上運行下列代碼:
System.out.println(InetAddress.getLocalHost().getHostName())
可以將其作為Java代碼執行,也可在喜歡使用的腳本語言(如BeanShell或Groovy)中作為一個具有Java風格語法的腳本執行。在獲取了主機的名稱之后,應該創建一個默認的/WEB-INF/applicationContext-[hostname].xml空文件夾(如我們上面所定義的),然后便可以開始了。
結束語
在本文中,我們提供了一系列的配置技巧,讓您在使用Spring MVC框架完成日常工作的時候更加輕松。如果您希望知道如何維護各種Web應用程序部署,可以試著找出最適合您的開發過程的解決方案。您的生活會更為輕松。
???????? 最近,買了本Spring入門書:spring In Action 。大致瀏覽了下感覺還不錯。就是入門了點。Manning的書還是不錯的,我雖然不像哪些只看Manning書的人那樣專注于Manning,但懷著崇敬的心情和激情通覽了一遍。又一次接受了IOC 、DI、AOP等Spring核心概念。 先就IOC和DI談一點我的看法。IOC(DI):其實這個Spring架構核心的概念沒有這么復雜,更不像有些書上描述的那樣晦澀。java程序員都知道:java程序中的每個業務邏輯至少需要兩個或以上的對象來協作完成,通常,每個對象在使用他的合作對象時,自己均要使用像new object() 這樣的語法來完成合作對象的申請工作。你會發現:對象間的耦合度高了。而IOC的思想是:Spring容器來實現這些相互依賴對象的創建、協調工作。對象只需要關系業務邏輯本身就可以了。從這方面來說,對象如何得到他的協作對象的責任被反轉了(IOC、DI)。
這是我對Spring的IOC的體會。DI其實就是IOC的另外一種說法。DI是由Martin Fowler 在2004年初的一篇論文中首次提出的。他總結:控制的什么被反轉了?就是:獲得依賴對象的方式反轉了。
如果對這一核心概念還不理解:這里引用一個叫Bromon的blog上找到的淺顯易懂的答案:
IoC與DI
首先想說說IoC(Inversion of Control,控制倒轉)。這是spring的核心,貫穿始終。所謂IoC,對于spring框架來說,就是由spring來負責控制對象的生命周期和對象間的關系。這是什么意思呢,舉個簡單的例子,我們是如何找女朋友的?常見的情況是,我們到處去看哪里有長得漂亮身材又好的mm,然后打聽她們的興趣愛好、qq號、電話號、ip號、iq號………,想辦法認識她們,投其所好送其所要,然后嘿嘿……這個過程是復雜深奧的,我們必須自己設計和面對每個環節。傳統的程序開發也是如此,在一個對象中,如果要使用另外的對象,就必須得到它(自己new一個,或者從JNDI中查詢一個),使用完之后還要將對象銷毀(比如Connection等),對象始終會和其他的接口或類藕合起來。
那么IoC是如何做的呢?有點像通過婚介找女朋友,在我和女朋友之間引入了一個第三者:婚姻介紹所。婚介管理了很多男男女女的資料,我可以向婚介提出一個列表,告訴它我想找個什么樣的女朋友,比如長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,然后婚介就會按照我們的要求,提供一個mm,我們只需要去和她談戀愛、結婚就行了。簡單明了,如果婚介給我們的人選不符合要求,我們就會拋出異常。整個過程不再由我自己控制,而是有婚介這樣一個類似容器的機構來控制。Spring所倡導的開發方式就是如此,所有的類都會在spring容器中登記,告訴spring你是個什么東西,你需要什么東西,然后spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的創建、銷毀都由 spring來控制,也就是說控制對象生存周期的不再是引用它的對象,而是spring。對于某個具體的對象而言,以前是它控制其他對象,現在是所有對象都被spring控制,所以這叫控制反轉。如果你還不明白的話,我決定放棄。
IoC的一個重點是在系統運行中,動態的向某個對象提供它所需要的其他對象。這一點是通過DI(Dependency Injection,依賴注入)來實現的。比如對象A需要操作數據庫,以前我們總是要在A中自己編寫代碼來獲得一個Connection對象,有了 spring我們就只需要告訴spring,A中需要一個Connection,至于這個Connection怎么構造,何時構造,A不需要知道。在系統運行時,spring會在適當的時候制造一個Connection,然后像打針一樣,注射到A當中,這樣就完成了對各個對象之間關系的控制。A需要依賴 Connection才能正常運行,而這個Connection是由spring注入到A中的,依賴注入的名字就這么來的。那么DI是如何實現的呢? Java 1.3之后一個重要特征是反射(reflection),它允許程序在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,spring就是通過反射來實現注入的。關于反射的相關資料請查閱java doc。
如果還不明白,放棄java吧!
如題。但 blogjava系統為何用.net開發
| |||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
---|---|---|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 | |||
4 | 5 | 6 | 7 | 8 | 9 | 10 | |||
11 | 12 | 13 | 14 | 15 | 16 | 17 | |||
18 | 19 | 20 | 21 | 22 | 23 | 24 | |||
25 | 26 | 27 | 28 | 29 | 30 | 31 | |||
1 | 2 | 3 | 4 | 5 | 6 | 7 |
常用鏈接
留言簿(1)
隨筆分類
隨筆檔案
文章檔案
相冊
友情鏈接
- struts2系列教程
- VMware中用NAT方式實現FreeBsd/Linux上網
- 工作流和權限
- 緩存
- 花非花
- 好的java技術blog
- 設計模式
搜索
最新評論

- 1.?re: 源碼分析:HashMap
- 評論內容較長,點擊標題查看
- --Web 2.0 技術資源
- 2.?wow gold
- 評論內容較長,點擊標題查看
- --wow gold
- 3.?wow gold
- WoW Gold,Buy Wow Gold,Cheap WoW Gold,wow power leveling,world of warcraft gold
- --wow gold
- 4.?re: 最近買了Spring in Action 中文版。
- 后面的例子引用的不錯,雖然不是很懂,所以還要繼續努力
- --Jedliu
- 5.?re: 最近買了Spring in Action 中文版。
- 這本書我也買了
- --壞男孩