介紹spring架構

Introducing to Spring Framework

作者:Rod Johnson

譯者:yanger,taowen

校對:taowen

 

關於Spring Framework,你可能已經聽見很多的議論。在本文中,我將試圖解釋Spring能完成什麼,和我怎麼會認爲它能幫助你開發J2EE應用程序。

又來一個framework?
你可能正在想“不過是另外一個的framework”。當已經有許多開放源代碼(和專有) J2EE framework時,爲什麼你還要耐下心子讀這篇文章或去下載Spring Framework?我相信Spring是獨特的,有幾個原因:它關注的領域是其他許多流行的Framework未曾關注的。Spring要提供的是一種管理你的業務對象的方法。

Spring既是全面的又是模塊化的。Spring有分層的體系結構,這意味着你能選擇僅僅使用它任何一個獨立的部分,而它的架構又是內部一致。因此你能從你的學習中,得到最大的價值。例如,你可能選擇僅僅使用Spring來簡單化JDBC的使用,或用來管理所有的業務對象。

它的設計從一開始就是要幫助你編寫易於測試的代碼。Spring是使用測試驅動開發的工程的理想框架。Spring不會給你的工程添加對其他的框架依賴。Spring也許稱得上是個一站式解決方案,提供了一個典型應用所需要的大部分基礎架構。它還涉及到了其他framework沒有考慮到的內容。

儘管它僅僅是一個從2003年2月纔開始的開源項目,但Spring有深厚的歷史根基。這個開源工程是起源自我在2002年晚些時候出版的《Expert One-on-One J2EE設計與開發》書中的基礎性代碼。這本書展示了Spring背後的基礎性架構思想。然而,對這個基礎架構的概念可以追溯到2000年的早些時候,並且反映了我爲一系列商業工程開發基礎結構的成功經驗。


2003年1月,Spring已經落戶於SourceForge上了。現在有10個開發人員,其中6個是高度投入的積極分子。

Spring架構上的好處

在我們進入細節之前,讓我們來看看Spring能夠給工程帶來的種種好處:Spring能有效地組織你的中間層對象,不管你是否選擇使用了EJB。如果你僅僅使用了Struts或其他爲J2EE的 API特製的framework,Spring致力於解決剩下的問題。

Spring能消除在許多工程中常見的對Singleton的過多使用。根據我的經驗,這是一個很大的問題,它降低了系統的可測試性和麪向對象的程度。

通過一種在不同應用程序和項目間一致的方法來處理配置文件,Spring能消除各種各樣自定義格式的屬性文件的需要。曾經對某個類要尋找的是哪個魔法般的屬性項或系統屬性感到不解,爲此不得不去讀Javadoc甚至源編碼?有了Spring,你僅僅需要看看類的JavaBean屬性。Inversion of Control的使用(在下面討論)幫助完成了這種簡化。

通過把對接口編程而不是對類編程的代價幾乎減少到沒有,Spring能夠促進養成好的編程習慣。 Spring被設計爲讓使用它創建的應用儘可能少的依賴於他的APIs。在Spring應用中的大多數業務對象沒有依賴於Spring。

使用Spring構建的應用程序易於單元測試。 Spring能使EJB的使用成爲一個實現選擇,而不是應用架構的必然選擇。你能選擇用POJOs或local EJBs來實現業務接口,卻不會影響調用代碼。 Spring幫助你解決許多問題而無需使用EJB。Spring能提供一種EJB的替換物,它們適用於許多web應用。例如,Spring能使用AOP提供聲明性事務管理而不通過EJB容器,如果你僅僅需要與單個數據庫打交道,甚至不需要一個JTA實現。 Spring爲數據存取提供了一個一致的框架,不論是使用的是JDBC還是O/R mapping產品(如Hibernate)。Spring確實使你能通過最簡單可行的解決辦法來解決你的問題。而這是有有很大價值的。

Spring做了些什麼?

Spring提供許多功能,在此我將依次快速地展示其各個主要方面。

任務描述

 首先,讓我們明確Spring範圍。儘管Spring覆蓋了許多方面,但我們對它應該涉什麼,什麼不應該涉及有清楚的認識。
 
 Spring的主要目的是使J2EE易用和促進好編程習慣。 Spring不重新輪子。因此,你發現在Spring中沒有logging,沒有連接池,沒有分佈式事務調度。所有這些東西均有開源項目提供(例如我們用於處理所有日誌輸出的Commons Logging以及Commons DBCP),或由你的應用程序服務器提供了。出於同樣的的原因,我們沒有提供O/R mapping層。對於這個問題已經有了像Hibernate和JDO這樣的優秀解決方案。
 
  Spring的目標就是讓已有的技術更加易用。例如,儘管我們沒有底層事務協調處理,但我們提供了一個抽象層覆蓋了JTA或任何其他的事務策略。 Spring沒有直接和其他的開源項目競爭,除非我們感到我們能提供新的一些東西。例如,象許多開發人員一樣,我們從來沒有對Struts感到高興過,並且覺得到在MVC web framework中還有改進的餘地。在某些領域,例如輕量級的IoC容器和AOP框架,Spring確實有直接的競爭,但是在這些領域還沒有已經較爲流行的解決方案。(Spring在這些領域是開路先鋒。) Spring也得益於內在的一致性。所有的開發者都在唱同樣的的讚歌,基礎想法依然與Expert One-on-One J2EE設計與開發中提出的差不多。 並且我們已經能夠在多個領域中使用一些中心的概念,例如Inversion of Control。 Spring在應用服務器之間是可移植的。當然保證可移植性總是一種挑戰,但是我們避免使用任何平臺特有或非標準的東西,並且支持在WebLogic,Tomcat,Resin,JBoss,WebSphere和其他的應用服務器上的用戶。
 
  Inversion of Control 容器
 
  Spring設計的核心是 org.springframework.beans 包, 它是爲與JavaBeans一起工作而設計的。 這個包一般不直接被用戶使用,而是作爲許多其他功能的基礎。
 
  下一個層面高一些的抽象是"Bean Factory"。一個Spring bean factory 是一個通用的Factory,它使對象能夠按名稱獲取,並且能管理對象之間的關係。 Bean factories 支持兩種模式的對象: Singleton:在此模式中,有一個具有特定名稱的共享對象實例,它在查找時被獲取。這是默認的,而且是最爲經常使用的。它對於無狀態對象是一種理想的模式。
 
  Prototype:在此模式中,每次獲取將創建一個獨立的對象。例如,這可以被用於讓用戶擁有他們自己的對象。由於 org.springframwork.beans.factory.BeanFactory是一個簡單的接口,它能被大量底層存儲方法實現。你能夠方便地實現你自己的BeanFactory,儘管很少用戶需要這麼做。最爲常用的BeanFactory定義是:XmlBeanFactory: 可解析簡單直觀的定義類和命名對象屬性的XML結構。 我們提供了一個DTD來使編寫更容易。
 
  ListableBeanFactoryImpl:提供瞭解析存放在屬性文件中的bean定義的能力,並且可通過編程創建BeanFactories。每個bean定義可能是一個POJO(通過類名和JavaBean初始屬性定義),或是一個FactoryBean。FactoryBean接口添加了一個間接層。通常,這用於創建使用AOP或其他方法的代理對象:例如,添加聲明性事務管理的代理。(這在概念上和EJB的interception相似,但實現得更簡單。) BeanFactories能在一個層次結構中選擇性地參與,繼承ancestor(祖先)的定義。這使得在整個應用中公共配置的共享成爲可能,雖然個別資源,如controller servlets,還擁有他們自己的獨立的對象集合。 這種使用JavaBeans的動機在《Expert One-on-One J2EE Design and Development》的第四章中有描述,在TheServerSide網站上的有免費的PDF版本(http://www.theserverside.com/resources/article.jsp?l=RodJohnsonInterview)。 通過BeanFactory概念,Spring成爲一個Inversion of Control的容器。(我不怎麼喜歡container這個詞,因爲它使人聯想到重量級容器,如EJB容器。Spring的BeanFactory是一個可通過一行代碼創建的容器,並且不需要特殊的部署步驟。)
 
  Inversion of Control背後的概念經常表述爲Hollywood原則的:“Don’t call me, I’ll call you。” IoC將控制創建的職責搬進了框架中,並把它從應用代碼脫離開來。涉及到配置的地方,意思是說在傳統的容器體系結構中,如EJB,一個組件可以調用容器並問“我需要它給我做工作的對象X在哪裏?”;使用IoC容器則只需指出組件需要X對象,在運行時容器會提供給它。容器是通過查看方法的參數表(例如JavaBean的屬性)做到的,也可能根據配置數據如XML。同樣原因,應用代碼更容易測試。JavaBean屬性是簡單的,屬於Java核心的,並且是容易測試的:僅編寫一個自包含的Junit測試方法用來創建對象和設置相關屬性即可。 一個好的IoC實現保留了強類型。如果你需要使用一個通用的factory來尋找合作者,你必須通過類型轉換將返回結果轉變爲想要的類型。這不是一個大不了的問題,但是不雅觀。使用IoC,你在你的代碼中表達了強類型依賴,框架將負責類型轉換。這意味着在框架配置應用時,類型不匹配將導致錯誤;在你的代碼中,你無需擔心類型轉換異常。
 
  大部分業務對象不依賴於IoC容器的APIs。這使得很容易使用遺留下來的代碼,且很容易的使用對象無論在容器內或不在容器內。例如,Spring用戶經常配置Jakarta Commons DBCP數據源爲一個Spring bean:不需要些任何定製代碼去做這件事。我們說一個IoC容器不是侵入性的:使用它並不會使你的代碼依賴於它的APIs。任何JavaBean在Spring bean factory中都能成爲一個組件。最後應該強調的是,IoC 不同於傳統的容器的體系結構,如EJB,應用代碼最小程度地依靠於容器。這意味着你的業務對象可以潛在的被運行在不同的IoC 框架上——或者在任何框架之外——不需要任何代碼的改動。 以我和其他Spring用戶的經驗來說,再怎麼強調IoC給應用程序代碼帶來的好處也不爲過。
 
  IoC不是一個新概念,但是它在J2EE團體裏面剛剛到達黃金時間。 有一些可供選擇的IoC 容器: 例如 Apache Avalon, PicoContainer 和 HiveMind。Avalon 從沒怎麼流行,儘管它很強大而且有很長的歷史。Avalon相當的重和複雜,並且看起來比新的IoC解決方案更具侵入性。 PicoContainer是一個輕量級而且更強調通過構造函數表達依賴性而不是JavaBean 屬性。 與Spring不同,它的設計允許每個類型一個對象的定義(可能是因爲它拒絕任何Java代碼外的元數據導致的侷限性)。在Spring, PicoContainer 和其他 IoC frameworks之間做比較,可參看文章Spring網站上的"The Spring Framework - A Lightweight Container"位於http://www.springframework.org/docs/lightweight_container.html。這個頁面裏面包含了PicoContainer站點的鏈接 。
 
  Spring BeanFactories 是非常輕量級的。用戶已經成功地將他們應用在applets和單獨的Swing應用中。(它們也很好地工作在EJB容器中。) 沒有特殊的部署步驟和察覺得到的啓動時間。這個能力表明一個容器在應用的任何層面幾乎立即可以發揮非常大的價值。Spring BeanFactory 概念貫穿於Spring始終, 而且是Spring如此內在一致的關鍵原因。在IoC容器中,Spring也是唯一的,它使用IoC作爲基礎概念貫穿於整個功能豐富的框架。
 
  對應用開發人員,最重要的是,一個或多個BeanFactory提供了一個定義明確的業務對象層。這類似於local session bean層,但比它更簡單。與EJBs不同,在這個層中的對象可能是相關的,並且他們的關係被擁有它們的factory管理。有一個定義明確的業務對象層對於成功的體系結構是非常重要的。可移植的文件和資源訪問XmlBeanFactory 例子
 
  Spring用戶通常在XML的“bean定義”文件中配置他們的應用。Spring的XML bean定義文檔的根是 元素。該元素包含一個或多個 定義。我們一般給每個bean定義的指定類和屬性。我們還必須指定ID作爲標識,這將成爲在代碼中使用該bean的名字。讓我們來看一個簡單的例子,它配置了三個應用程序對象,之間的關係在J2EE應用中常常能夠看到:J2EE DataSource 使用DataSource的DAO 在處理過程中使用DAO的業務對象在下面的例子中,我們使用一個來自Jakarta Commons DBCP項目的BasicDataSource。這個class(和其他許多已有的class一樣)可以簡單地被應用在Spring bean factory中,只要它提供了JavaBean格式的配置。需要在shutdown時被調用的Close方法可通過Spring的"destroy-method"屬性被註冊,以避免BasicDataSource需要實現任何Spring 的接口。
 
  代碼:
  <beans>

  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
    <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
    <property name="url"><value>jdbc:mysql://localhost:3306/mydb</value></property>
    <property name="username"><value>root</value></property>
  </bean>

BasicDataSource中我們感興趣的所有屬性都是String類型的,因此我們用<value>元素來指定他們的值。如果必要的話,Spring使用標準的 JavaBean屬性編輯器機制來把String轉換爲其他的類型。

現在,我們定義DAO,它有一個對DataSource的bean引用。Bean間關係通過<ref>元素來指定:

代碼:

<bean id="exampleDataAccessObject"
      class="example.ExampleDataAccessObject">
    <property name="dataSource"><ref bean="myDataSource"/></property>
  </bean>

The business object has a reference to the DAO, and an int property (exampleParam):
<bean id="exampleBusinessObject"
      class="example.ExampleBusinessObject">
    <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
    <property name="exampleParam"><value>10</value></property>
  </bean>

</beans>

對象間的關係一般在配置中明確地設置,象這個例子一樣。我們認爲這樣做是件好事情。然而Spring還提供了我們稱做"autowire"的支持, 一個la PicoContainer,其中它指出了bean間的依賴關係。這樣做的侷限性——PicoContainer也是如此——是如果有一個特殊類型的多個Bean,要確定那個類型所依賴的是哪個實例是不可能。好的方面是,不滿足的依賴可以在factory初始化後被捕獲到。(Spring 也爲顯式的配置提供了一種可選的依賴檢查,它可以完成這個目的)

在上面的例子中,如果我們不想顯式的編寫他們的關係,可使用如下的autowire特性:

代碼:

<bean id="exampleBusinessObject"
   class="example.ExampleBusinessObject"
   autowire="byType">

    <property name="exampleParam"><value>10</value></property>
 </bean>
 BasicDataSource中我們感興趣的所有屬性都是String類型的,因此我們用<value>元素來指定他們的值。如果必要的話,Spring使用標準的 JavaBean屬性編輯器機制來把String轉換爲其他的類型。

 

現在,我們定義DAO,它有一個對DataSource的bean引用。Bean間關係通過<ref>元素來指定:

代碼:

<bean id="exampleDataAccessObject"
      class="example.ExampleDataAccessObject">
    <property name="dataSource"><ref bean="myDataSource"/></property>
  </bean>

The business object has a reference to the DAO, and an int property (exampleParam):
<bean id="exampleBusinessObject"
      class="example.ExampleBusinessObject">
    <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
    <property name="exampleParam"><value>10</value></property>
  </bean>

</beans>

對象間的關係一般在配置中明確地設置,象這個例子一樣。我們認爲這樣做是件好事情。然而Spring還提供了我們稱做"autowire"的支持, 一個la PicoContainer,其中它指出了bean間的依賴關係。這樣做的侷限性——PicoContainer也是如此——是如果有一個特殊類型的多個Bean,要確定那個類型所依賴的是哪個實例是不可能。好的方面是,不滿足的依賴可以在factory初始化後被捕獲到。(Spring 也爲顯式的配置提供了一種可選的依賴檢查,它可以完成這個目的)

在上面的例子中,如果我們不想顯式的編寫他們的關係,可使用如下的autowire特性:

代碼:

<bean id="exampleBusinessObject"
   class="example.ExampleBusinessObject"
   autowire="byType">

    <property name="exampleParam"><value>10</value></property>
 </bean>

 

使用這個特性,Spring會找出exampleBusinessObject的dataSource屬性應該被設置爲在當前BeanFactory中找到的DataSource實現。在當前的BeanFactory中,如果所需要類型的bean不存在或多於一個,將產生一個錯誤。我們依然要設置exampleParam屬性,因爲它不是一個引用。

Autowire支持和依賴檢查剛剛加入CVS並將在Spring 1.0 M2(到10/20,2003)中提供。本文中所討論的所有其他特性都包含在當前1.0 M1版本中。

 

 

發佈了68 篇原創文章 · 獲贊 2 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章