企業開發框架NHibernate和Spring.Net簡介

在開始介紹NHibernate和Spirng.Net之前,有必要先介紹一下相對應的兩個Java開源框架Hibernate和Spring。它們是在Java的Web應用開發中已經很成熟的框架,而前者則是兩個還不成熟的.Net中的開源框架,它們在SourceForge開源社區中正在得到不斷的補充和完善,其最終目標就是要在.NET環境下實現Hibernate和Spring的全部  特性。
Spring是一個解決了許多在J2EE開發中常見問題的強大框架,它提供了管理業務對象的一致方法並且鼓勵注入對接口編程而不是對類編程的良好習慣。Spring提供了唯一的數據訪問抽象,包括簡單和有效率的JDBC框架,極大改進了效率並且減少了可能的錯誤。Spring的數據訪問架構還集成了Hibernate和其他O/R mapping解決方案。Spring還提供了唯一的事務管理抽象,它爲各種底層事務管理技術,例如JTA或者JDBC事務提供一個一致的編程模型。Spring提供了一個用標準Java語言編寫的AOP框架,它給POJO提供了聲明式的事務管理和其他企業事務。這個框架足夠強大,使得應用程序能夠拋開EJB的複雜性,同時享受着和傳統EJB相關的關鍵服務。Spring還提供了可以和IoC容器集成的強大而靈活的MVC Web框架。
Hibernate是一個開放源代碼的對象關係映射(Object-Relation Mapping,ORM)框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲地使用對象編程思維來操縱數據庫。Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。鑑於Hibernate在Java社區巨大的影響力,它其實已經成爲Java社區的持久層技術事實上的標準。
基於.NET的開發框架NHibernate和Spring.Net分別來源於上述提到的兩個Java框架,框架結構和實現功能都基本一致,只不過應用環境從Java轉到了.NET。到目前爲止NHibernate和Spring.NET仍然處於完善階段,不是特別成熟穩定,但是具有Hibernate和Spring的強大背景,使得它們有着很光明的未來。事實上,現在NHibernate和Spring.NET正在被越來越多的開發人員所熟知,並且有些開發人員已經開始嘗試在自己的項目中使用這兩個框架。
1.5.1  企業開發框架的優勢
在當今的企業環境中,開發企業應用程序是一個相當繁瑣、浪費時間的過程,爲了讓這個過程變得相對簡單且高效,一些開源的、輕量級的框架正在被廣泛的使用,比如Java下的Structs、Spring、Hibernate、iBATIS以及.NET下的Spring.Net和NHibernate等。
它們的廣泛使用降低了系統開發的複雜度,弱化了系統各模塊間的耦合程度,縮短了開發週期,增強了系統的可維護性和可擴展性。“站在巨人的肩膀上”是牛頓的一句名言,它已經成爲google學術搜索引擎的座右銘,同樣也爲筆者所推崇。筆者對.Net軟件開發平臺的設想如下,採用開源的ORM框架做數據持久層,ASP.NET沒有合適的Web層框架,採用Asp.Net的Code-behind方式編寫代碼,數據持久層同Web表現層之間的連接採用IoC容器。
衡量了多種框架以後,最終筆者推薦採用的技術架構就是NHibernate+Spring.Net+ASP.NET,下面對NHibernate和Spring.net分別進行比較詳細的介紹。
1.5.2  NHibernate介紹
在今日的企業環境中,把面向對象的軟件和關係數據庫一起使用可能是相當麻煩和浪費時間的。NHibernate是一個面向.NET環境的對象/關係數據庫映射工具。對象/關係數據庫映射(object/relational mapping,ORM)這個術語表示一種技術,用來把對象模型表示的對象映射到基於SQL的關係模型數據結構中去。
NHibernate不僅僅管理.NET類到數據庫表的映射(包括.NET 數據類型到SQL數據類型的映射),還提供數據查詢和獲取數據的方法,可以大幅度減少開發時人工使用SQL和ADO.NET處理數據的時間。
NHibernate的目標主要是用於與數據持久化相關的編程任務,能夠使開發人員從原來枯燥的SQL語句的編寫中解放出來,解放出來的精力可以讓開發人員投入到業務邏輯的實現上。對於以數據爲中心的程序,開發人員往往是在數據庫中使用存儲過程來實現商業邏輯,這種情況下NHibernate可能不是最好的解決方案,但對於那些基於.NET,並且能夠實現OO業務模型和商業邏輯的中間層應用,NHibernate是最有用的。NHibernate可以幫助用戶消除或者包裝那些針對特定廠商的SQL代碼,並且幫用戶把結果集從表格式的表示形式轉換成一系列的對象。
1.NHibernate體系結構
NHibernate體系結構如圖1-51所示。它體現了NHibernate如何使用數據庫和配置文件數據來爲應用程序提供持久化服務(和持久化的對象)。
在NHibernate全面解決體系中,NHibernate運行時體系結構的細節如圖1-52所示。
      
     圖1-51  NHibernate體系結構                      圖1-52  NHibernate詳細體系結構
*    SessionFactory(NHibernate.IsessionFactory):它是Session的工廠,是ConnectionProvider的客戶。可以持有一個可選的(第二級)數據緩存,可以在進程級別或集羣級別保存的可以在事物中重用的數據。
*    會話(NHibernate.ISession):單線程,生命期較短的對象,代表應用程序和持久化層之間的一次對話。封裝了一個ADO.NET連接,也是Transaction的工廠。保存有必需的(第一級)持久化對象的緩存,用於遍歷對象圖,或者通過標識符查找對象。
*    持久化對象(Persistent)及其集合(Collections):生命期較短的單線程的對象,包含了持久化狀態和商業功能。這些可能是普通的對象,唯一特別的是現在從屬於且僅從屬於一個Session。一旦Session被關閉,它們都將從Session中取消聯繫,可以在任何程序層自由使用(比如,直接作爲傳送到表現層的DTO,數據傳輸對象)。
*    臨時對象(Transient Object)及其集合(Collection):目前沒有從屬於一個Session的持久化類的實例。這些可能是剛剛被程序實例化,還沒有來得及被持久化,或者是被一個已經關閉的Session實例化。
*    事務Transaction (NHibernate.ITransaction):(可選)單線程,生命期較短的對象,應用程序用其來表示一批工作的原子操作,它是底層的ADO.NET事務的抽象。一個Session在某些情況下可能跨越多個Transaction事務。
*    ConnectionProvider(NHibernate.Connection.ConnectionProvider):(可選)ADO.NET連接的工廠。從底層的IDbConnection抽象而來。對應用程序不可見,但可以被開發者擴展/實現。
*    TransactionFactory(net.sf.hibernate.TransactionFactory):(可選)事務實例的工廠。對應用程序不可見,但可以被開發者擴展/實現。
2.持久化類
持久化類是應用程序用來解決商業問題的類(比如,在電子交易程序中的Customer和Order)。持久化類是暫時存在的,實例會被持久性保存於數據庫中。
如果這些類符合簡單的規則,NHibernate能夠工作得最好,這些規則就是Plain Old CLR Object(POCO,簡單傳統CLR對象)編程模型。
POCO簡單示例(用一個類描述一隻貓)如下:
#001    public class Cat
#002    {
#003       private long _id;                  // 標識符
#004       private string _name;             // 名字
#005       private DateTime _birthdate;        // 出生日期
#006       private Cat _mate;                // 貓的配對
#007       private Set _kittens;                 // 貓仔
#008       private Color _color;               // 顏色
#009       private char _sex;              // 性別
#010       private float _weight;             // 體重
#011           public long Id              // 標識符屬性
#012      {  get { return _id; }
#013              set { _id = value; }
#014       public string Name              // 名字屬性
#015           {  get { return _name; }
#016          set { _name = value; }}
#017       public DateTime Birthdate          // 出生日期屬性
#018           {  get { return _birthdate; }
#019              set { _birthdate = value; }}
#020       public Cat Mate                 // 貓的配對屬性
#021           {  get { return _mate; }
#022         set { _mate = value; }}
#023       public Set Kittens              // 貓仔屬性
#024           {  get { return _kittens; }
#025              set { _kittens = value; }}
#026       public Color Color              // 顏色屬性
#027           {  get { return _color; }
#028              set { _color = value; }}
#029       public char Sex                 // 性別屬性
#030           {  get { return _sex; }
#031              set { _sex = value; }}
#032       public float Weight             // 體重屬性
#033           {  get { return _weight; }
#034              set { _weight = value; }}
#035    }
有4條主要的規則如下。
*    爲持久化字段聲明訪問器(getters和setters)。
*    Cat爲它的所有可持久化字段聲明瞭getters和setters訪問器。用訪問器來替代直接訪問字段是個好習慣。也可以通過字段(field)來使用NHibernate。屬性不一定需要聲明爲public的。NHibernate可以對default、protected、internal或private的屬性執行持久化。
*    實現一個默認的構造方法(constructor):Cat有一個顯式的無參數默認構造方法。所有的持久化類都必須具有一個默認的構造方法(可以不是public的),這樣NHibernate就可以使用Constructor.Invoke()來實例化它們。
*    提供一個標識屬性(identifier property)(可選)。
*    建議使用不是sealed的類(可選)。
3.NHibernate映射
對象和關係數據庫之間的映射是用一個XML文檔(XML document)來定義的。這個映射文檔被設計爲易讀的,並且可以手工修改。映射語言是以.NET爲中心的,意味着映射是按照持久化類的定義來創建的,而非表的定義。在Hibernate中XML映射文檔可以手動定義,也有一些工具來輔助生成,包括Xdoclet、Middlegen和AndroMDA,但是在NHibernate文檔中並沒有上述的輔助工具,不過可以採用MyGeneration這樣的代碼生成工具來生成XML配置文檔。下面是一個映射的例子:
#001  <?xml version="1.0" ?>
#002  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" namespace="Eg" assembly="Eg">
#003  <class name="Cat" table="CATS" discriminator-value="C">
#004       <id name="Id" column="uid" type="Int64">
#005          <generator class="hilo"/>
#006       </id>
#007       <discriminator column="subclass" type="Char"/>
#008       <property name="Name" type="Char"/>
#009       <property name="Color" not-null="false"/>
#010       <property name="Sex" not-null="false" update="false"/>
#011       <property name="Weight"/>
#012       <many-to-one name="Mate" column="mate_id"/>
#013       <set name="Kittens">
#014          <key column="mother_id"/>
#015          <one-to-many class="Cat"/>
#016       </set>
#017       <subclass name="DomesticCat" discriminator-value="D">
#018          <property name="Name" type="String"/>
#019       </subclass>
#020  </class>
#021  </hibernate-mapping>
所有的XML映射都需要使用nhibernate-mapping-2.0 schema。目前的schema可以在NHibernate的資源路徑或者是NHibernate.dll的嵌入資源(Embedded Resource)中找到。NHibernate總是會優先使用嵌入在資源中的schema文件。在使用VisualStudio.NET時,用戶應該將hibernate-mapping拷貝到C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\ Packages\schemas\xml路徑中,以獲得智能感知功能。
#002行:hibernate-mapping這個元素包括4個可選的屬性。schema屬性,指明瞭這個映射所引用的表所在的schema名稱。假若指定了這個屬性,表名會加上所指定的schema的名字擴展爲全限定名。假若沒有指定,表名就不會使用全限定名。default-cascade指定了未明確註明cascade屬性的屬性和集合類會採取什麼樣的默認級聯風格。auto-import屬性默認在查詢語言中可以使用非全限定名的類名。default-access告訴用戶怎麼訪問屬性值。假若有兩個持久化類,它們的非全限定名是一樣的,應該設置auto-import= "false"。假若把一個“import”過的名字同時對應兩個類,NHibernate會拋出一個異常。
#003行:使用class元素來定義一個持久化類。name是持久化類(或者接口)的全限定名。table是對應的數據庫表名。discriminator-value(可選 - 默認和類名一樣)是一個用於區分不同的子類的值,在有多態行爲時使用。
#004行:使用id來標識主鍵字段,因爲被映射的類必須聲明對應數據庫表主鍵字段。大多數類有一個屬性,爲每一個實例包含唯一的標識。<id> 元素定義了該屬性到數據庫表主鍵字段的映射。
#005行:必須聲明的 <generator> 子元素是一個.NET類的名字,用來爲該持久化類的實例生成唯一的標識。其中hilo(高低位)使用一個高/低位算法來高效地生成Int64、Int32 或者Int16類型的標識符。給定一個表和字段(默認分別是hibernate_unique_key 和next)作爲高位值的來源。高/低位算法生成的標識符只在特定的數據庫中是唯一的。
#007行:在“一棵對象繼承樹對應一個表”的策略中,<discriminator>元素是必需的,它聲明瞭表的識別器字段。識別器字段包含標誌值,用於告知持久化層應該爲某個特定的行創建哪一個子類的實例。只能使用如下受到限制的類型:String,Char,Int32,Byte,Int16,Boolean,YesNo,TrueFalse。
#008-011行:<property>元素爲類聲明瞭一個持久化的屬性。name是屬性的名字,type(可選)是一個NHibernate類型的名字,not-null(可選)表明這個屬性是否爲空,update(可選-默認爲true)表明在用於UPDATE的SQL語句中是否包含這個字段。
#012行:通過many-to-one元素,可以定義一種常見的與另一個持久化類的關聯。這種關係模型是多對一關聯(實際上是一個對象引用)。
#013-016行:<set>爲集合類標識,其中discriminator-value(可選 - 默認爲類名)用於區分每個獨立的子類的值。
#017行:子類(subclass),多態持久化需要爲父類的每個子類都進行聲明。對於上面建議的“每一棵類繼承樹對應一個表”的策略來說,就需要使用<subclass>聲明。
4.NHibernate應用實例
上面已經編寫好了Cat類和相應的映射文件,爲了正確調用Nhibernate,必須對web.config配置文件進行配置。具體配置代碼如下:
#001  <xml version="1.0" encoding="utf-8" ?>
#002  <configuration>
#003       <configSections>
#004           <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
#005       </configSections>
#006       <nhibernate>
#007           <add
#008                key="hibernate.connection.provider"
                     value="NHibernate.Connection.DriverConnectionProvider" />
#009           <add
#010                key="hibernate.connection.driver_class"
                    value="NHibernate.Driver.SqlClientDriver" />
#011           <add
#012                key="hibernate.connection.connection_string"
                    value="server=localhost;uid=sa;pwd=123;database=Sample" />
#013           <add
#014                key="hibernate.connection.isolation" value="ReadCommitted" />
#015           <add
#016                key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
#017       </nhibernate>
#018  </configuration>
#012行:hibernate.connection.connection_string的value爲連接服務器ConnectoinString。其他的都是Nhibernate運行所必需的配置信息。
配置完web.config文件,在測試工程中引用NHibernate.dll和Eg,具體代碼如下。
#001    using System;
#002    using System.Collections.Generic;
#003    using System.Text;
#004   using NHibernate;
#005    using NHibernate.Cfg;
#006    using POJOLibrary;
#007    namespace Test
#008    {
#009        class Program
#010        {
#011            static void Main(string[] args)
#012            { Configuration c = new Configuration();
#013                c.AddAssembly("Eg");
#014                ISessionFactory factory = c.BuildSessionFactory();
#015               ISession s = factory.OpenSession();
#016               ITransaction t = s.BeginTransaction();
#017                Cat cat = new Cat();
#018                cat.Name = “Bolo”;
#019                 cat.Color = “Black”;
#020                 cat.Sex = “Male” ;
#021                 cat.Weight = “12” ;
#022                s.Save(cat);
#023                t.Commit();}
#024        }
#025    }
NHibernate是通過Isession的實現類來提供功能的,上面代碼通過Itransaction打開一個事務t=s.BeginTransaction(),最後通過調用t.Commit()提交事務,將數據寫入數據庫中。
通過上述NHibernate的一個簡單的應用實例可以看到,整個應用程序中沒有寫任何一句SQL語句。
雖然NHibernate目前不是非常的成熟,還沒有達到實用化的水平,它經常在負荷有些大的時候崩潰,但是它脫胎於在Java社區有着巨大影響力的Hibernate,而且NHibernate在實際應用方面確實體現出了它的優勢,基於上述原因NHibernate一定會被越來越多的開發人員所接受,而且一定會被標準化。到目前爲止,NHibernate的最新版本爲1.1,官方站點[url]http://nhibernate.sourceforge.net/[/url]上目前的版本是beta 0.7.0.0。
1.5.3  Spring.Net介紹
Spring.Net是一個關注於.NET企業應用開發的應用程序框架,它能夠提供非常豐富的功能,例如依賴注入(Dependency Injection),面向方面編程(Aspect Oriented Programming),數據訪問抽象以及ASP.NET集成等。Spring.NET脫胎於Java的Spring框架,其1.0版包括了一個功能完整的反轉控制容器和AOP函數庫,在後續的版本中將包含對ASP.NET、Remoting和數據訪問的支持。Spring.Net的框架如圖1-53所示。
圖1-53  Spring.Net的框架圖
1.控制反轉
控制反轉(IoC,Inversion of Control)意味着將設計好的交給系統去控制,而不是在自己的類內部控制。
IoC是近年來興起的一種思想,主要是協調各組件間相互的依賴關係,同時大大提高了組件的可移植性,組件的重用機會也變得更多。在傳統的實現中,由程序內部代碼來控制程序之間的關係。我們經常使用new關鍵字來實現兩組鍵間關係的組合,這種實現地方式會造成組件之間耦合(一個好的設計,不但要實現代碼重用,還要將組件間關係解耦)。IoC很好地解決了該問題,它將實現組件間關係從程序內部提到外部容器,也就是說由容器在運行期將組件間的某種依賴關係動態注入組件中。
分離關注(Separation of Concerns:SOC)是產生IoC模式和AOP的最原始動力,通過功能分解可得到關注點,這些關注可以是組件、方面或服務。
GOF設計模式中,我們已經習慣一種思維編程方式——接口驅動。接口驅動有很多好處,可以提供靈活的子類實現,增加代碼穩定和健壯性等,但是接口一定是需要實現的,即如下語句一定要執行。
AInterface a = new AInterfaceImp();
AInterfaceImp是接口AInterface的子類,Ioc模式可以根據需要延緩接口的實現,有個比喻:接口如同空的模型套,在必要時向模型套注射石膏,成爲一個模型實體。可以人爲控制接口的實現完成“注射”。
IoC的實現方式有以下幾種:
*    基於接口的(Interface-based IoC,Type-1)。
*    基於設值的(Setter-based IoC,Type-2)。
*    基於構造的(Construtor-based IoC,Type-3)。
下面通過簡單的例子分別介紹上述幾種實現方式。
(1)Type-1。基於接口的設計方法通常是利用接口將調用者與實現者分離。
#001  public class Sport {
#002  private InterfaceBall ball; //InterfaceBall是定義的接口
#003  public void init() {
#004  //Basketball實現了InterfaceBall接口
#005  ball = (InterfaceBall) Class.forName("Basketball").newInstance();}
#006  }
Sport類在編譯期依賴於InterfaceBall的實現,爲了將調用者與實現者分離,可以動態生成Basketball類並將其強制類型轉換爲InterfaceBall。
(2)Type-2。基於設值的設計方法是通過在類中暴露setter方法來實現依賴關係。
#001  public class Sport {
#002  private InterfaceBall _ball;
#003  public InterfaceBal1 ball
#004  {
#005      set{ _ball = value ;} }
#006  }
Spring.NET就是實現了該類型的輕量級容器。
(3)Type-3。通過構造方法完成依賴關係。
#001  public class Sport {
#002  private InterfaceBall ball;
#003  public Sport(InterfaceBall arg) {
#004  ball = arg; }
#005  }
由於Type-3在構造期就形成了對象的依賴關係,所以對對象的重用變得困難。有些框架需要組件提供一個默認的構造方法,此時就顯現出Type-3的侷限性。通常所有的參數都是通過構造方法注入的,當對象間的依賴關係較多時,構造方法就顯得比較複雜,不利於單元測試。PicoContainer就是實現了Type-3依賴注入模式的輕量級容器。
2.Spring.NET庫
Spring.NET庫有6個基本組成部分,基本上涵蓋了Spring.NET框架的所有功能結構。
*    Spring.Core庫是Spring.NET框架最基礎的部分,它提供了依賴注入的功能。Spring.NET中大部分的函數庫都依賴於這個核心庫提供的功能,或者是對核心庫的擴展。IObjectFactory是核心容器接口,負責管理容器內的注入對象,而IApplicationContext則是IObjectFactory的繼承,它擴展了一些功能。
*    Spring.Aop庫爲商業邏輯對象提供了面向方面編程的支持,它爲創建企業應用和爲商業對象提供服務打下了基礎,是Spring核心庫中IoC容器的補充。
*    Spring.Web庫爲ASP.NET增加了很多功能,例如ASP.NET頁面的依賴注入,數據雙向綁定,爲ASP.NET提供母版頁功能,增強了本地化支持。所有的這些都是對ASP.NET很好的擴展。
*    Spring.Services庫可以將任何一個“普通”對象(“普通”是指該對象不是繼承自特殊服務的基類)暴露成爲一個企業應用(COM+)或者遠程對象。因爲對依賴注入和原數據屬性重載的支持,.NET 中的Web服務會獲得更好的配置上的靈活性。同樣,該庫也提供了對Windows服務的支持。
*    Spring.Data庫爲.NET提供了一個數據訪問層的抽象,它能夠用於從ADO.NET到多種ORM Provider的數據訪問提供者。它同時包含了一個ADO.NET抽象層,簡化了對ADO.NET的編碼和事務管理。
*    Spring.ORM庫提供了一個用於常見的對象—關係映射庫的綜合層,它提供了諸如對事務管理的支持等功能。
3.面向方面編程(AOP)
面向方面編程是對面向對象編程(OOP)的補充,是另外一種思考編程框架的方法。面向對象是將應用分解成具有層次結構的對象;而面向方面編程則是把程序分解成方面或者關注點,使諸如事務管理等方面的模塊化成爲可能。Spring.NET中很關鍵的一個組件就是AOP框架。能夠幫助Spring.NET的 IoC容器爲企業應用提供一個非常強大的中間件解決方案。
AOP用於Spring.NET可以完成下列功能。
*    提供公開的企業服務,尤其是作爲COM+公開服務的替代者。這些服務中最重要的服務是公開的事務管理,這是Spring.NET事務抽象的基礎。
*    允許用戶實現定製的方面,通過面向方面編程來補充面向對象編程的不足。
用戶不但可以把Spring.NET AOP看作是能夠不通過COM+就可以提供公開事務管理的技術,而且還可以充分發揮Spring.NET AOP框架的功能區實現定製方面。
通過上面的介紹讀者可能對AOP已經有了一個大致的瞭解,下面介紹幾個關於AOP的概念。
*    方面(Aspect):這個是一個讓讀者感覺比較模糊的概念,它和通常意義上的方面不完全一樣,它是對關注點的模塊化,這可能會橫切多個對象。事務管理是一個非常好的橫切關注點企業應用的例子。在Spring.NET中,方面作爲建議者或者監聽器的形式實現。
*    連接點(Jointpoint):程序運行期間的一些點,例如方法調用或者特殊的異常被拋出。
*    建議(Advice):AOP框架在一個特殊連接點上採取的動作。這些不同類型的建議包括“around”、“before”和“throws” 等建議。很多AOP框架,包括Spring.NET,都把一個建議模擬成一個監聽器,同時維護一個“around”連接點的監聽器鏈。
*    切點(Pointcut):一組連接點,用於指定建議應該激活的時間。一個AOP框架必須能夠允許開發人員指定切點,例如,使用正則表達式。
*    介紹(Introduction):添加方法或域到建議類。Spring.NET允許介紹一個新的接口到任何一個建議對象中。例如,爲了簡化對對象狀態變化的跟蹤,可以使用建議爲任何對象實現一個IAuditable接口。
*    目標對象:包含連接點的對象。
*    AOP代理:由AOP框架創建的對象,包括建議。在Spring.NET中,一個AOP代理是一個在運行期使用IL代碼生成的動態代理。
*    Weaving:裝配對象創建一個被建議對象。裝配工作發生在編譯期(例如使用Gripper-Loom .NET編譯器),也能發生在運行期。Spring.NET在運行期執行裝配動作。
4.Spring.NET應用實例
下面以經典的Movie Finder作爲Spring.NET應用實例來講解IoC容器的使用方法。實例的C#代碼可以在Spring.NET發佈版的examples/Spring/Spring.Examples.MovieFinder目錄中找到。
(1)Movie Finder。MovieFinder例子的起始類是MovieApp類,這是具有單一應用程序入口點的普通.NET類。代碼如下所示:
#001  using System;
#002  namespace Spring.Examples.MovieFinder
#003  {
#004      public class MovieApp
#005      {
#006           public static void Main ()
#007           {
#008           }
#009      }
#010  }
現在想做的是獲得一個對MovieFinder類實例的引用。這是Spring.NET例子,所以要從Spring.NET的IoC容器類IApplicationContext獲得這個引用。應用程序配置文件中的IApplicationContext配置信息如下:
#001  <?xml version="1.0" encoding="utf-8" ?>
#002  <configuration>
#003  <configSections>
#004  <sectionGroup name="spring">
#005  <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
#006  <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
#007  </sectionGroup>
#008  </configSections>
#009  <spring>
#010  <context>
#011  <resource uri="config://spring/objects"/>
#012  </context>
#013  <objects>
#014  <description>An example that demonstrates simple IoC features.</description>
#015  </objects>
#016  </spring>
#017  </configuration>
將在應用程序示例中用到的對象配置成嵌套在<objects/>元素中的<object/>元素。
(2)獲得IApplicationContext應用。代碼如下所示:
#001  using System;
#002  using System.Configuration;
#003  using Spring.Context;
#004  ...
#005  public static void Main ()
#006  { IApplicationContext ctx = ContextRegistry.GetContext();}
#007  ...
如上所述,using System.Configuratoin和using Spring.Context兩條using語句被加到了MovieApp類文件中。其中,System.Configuration命名空間可以讓應用程序存取保存在配置文件中的Spring.NET的IoC容器的定義;Spring.Context命名空間可以讓應用程序存取IApplicationContext類,這個類是應用程序存取Spring.NET所提供的功能的主要方法。
方法main獲得一個IApplicationContext的實現,它的配置信息已經被寫在應用程序配置文件內名爲<objects/>的節中。
(3)第一個對象的定義。到目前爲止,在應用程序配置文件中還沒有對象定義,所以下面定義一個對象。MovieLister實例的XML定義如下所示。
#001  ...
#002  <objects>
#003    <object name="MyMovieLister"
#004           type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">
#005    </object>
#006  </object>
#007  ...
#003行:MyMovieLister是該對象的唯一標識符,使用這個標識符,這個對象的實例就能夠被如下所示的代碼中的IApplicationContext引用所獲取。
#001  ...
#002  public static void Main ()
#003    { IApplicationContext ctx = ContextRegistry.GetContext();
#004      MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");}
#005  ...
lister實例仍然沒有與IMovieFinder接口相應的實現注入。此時如果使用MoviesDirectedBy方法會導致NullReferenceException異常發生,因爲lister實例仍然還沒有對IMovieFinder的引用。注入到lister實例中的IMovieFinder接口實現的XML配置定義如下。
#001  ...
#002     <objects>
#003        ...
#004        <object name="MyMovieFinder"
#005       type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>
#006        </object>
#007        ...
#008     </object>
#009  ...
(4)設值注入。下面要做的是把以MyMovieFinder爲標識符的IMovieFinder實例注入到以MyMovieLister爲標識符的MovieLister實例中。這個注入動作使用設值注入的方法,代碼如下所示:
#001  ...
#002  <objects>
#003  <object name="MyMovieLister"
#004  type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">
#005  <!-- using setter injection... -->
#006  <property name="movieFinder" ref="MyMovieFinder"/>
#007  </object>
#008  <object name="MyMovieFinder"
#009  type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>
#010  </object>
#011  </objects>
#012  ...
當IApplicationContext接口取得了MyMovieLister對象以後,Spring.NET的IoC容器將會把對MyMovieLister對象的引用注入到MyMovieLister對象的MovieFinder屬性中去。這樣,在程序中引用的MovieFinder對象即配置完畢,可以列出某導演導過的所有電影。
#001  ...
#002  public static void Main ()
#003  {
#004  IApplicationContext ctx = ContextRegistry.GetContext();
#005  MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");
#006  Movie[] movies = lister.MoviesDirectedBy("Roberto Benigni");
#007  Console.WriteLine ("\nSearching for movie...\n");
#008  foreach (Movie movie in movies)
#009  {Console.WriteLine (
#010   string.Format ("Movie Title = '{0}', Director = '{1}'.",
#011   movie.Title, movie.Director));}
#012  Console.WriteLine ("\nMovieApp Done.\n\n");
#013  }
#014  ...
也可以使用構造注入的方法,讀者可以自己考慮構造注入方法的實現方式,這裏就不再具體介紹。
現在Spring.Net的1.0.2版本已經發布,讀者可以通過訪問[url]http://sourceforge.net/[/url] projects/ springnet下載。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章