Spring.Net 一直講求 ” 配置應用程序“,有一種需求在實際開發中不太常用,卻非常有用 -> 配置對象的行為,Spring.Net 也能夠完美的實現。Spring.Net 通過幾個專門的接口來控制容器中對象的行為。比如說,我們可以自定義對象初始化行為,自定義對象銷毀行為,抽象對象定義和子對象定義等。在下面會介紹常用的幾中 自定義對象行為:
① 自定義對象初始化行為
② 自定義對象銷毀行為
③ 抽象對象定義
④ 子對象定義
⑤ IObjectPostProcessor及擴展類(對象后處理器)
一 . 自定義初始化行為和銷毀行為
Spring.Net 通過幾個專門的接口來控制容器中對象的行為,這些接口包括 Spring.Net 定義的 IInitializingObject接口 和標准的 System.IDisposable 接口。容器會在實現了這兩個接口的對象上調用 AfterPropertyiesSet()方法 和 Dispose()方法,這樣我們就有機會在對象初始化和銷毀時做一些額外的工作。
通過 Spring.Net 定義的 IInitializingObject接口 可以實現自定義初始化行為。在VS中通過對象瀏覽器我們可以看到 Spring.Objects.Factory.IInitializingObject 接口,只有一個方法:
void AfterPropertiesSet:在對象的所有屬性被設置之后由容器調用。在該方法中,我們可以檢查必需的屬性是否都已被正確設值,或者可以執行進一步的初始化工作。此方法可以拋出任何異常以標識配置錯誤、初始化失敗等情況。
我們前面討論了如何初始化,接下來我們討論通過標准的 System.IDisposable接口 可以實現自定義銷毀行為。通過 System.IDisposable接口 使我們有機會在容器被銷毀時,通過對象的回調方法來處理容器中對象的銷毀工作。該接口只有一個方法:
void Dispose():當容器被銷毀時調用。在此可釋放對象保留的任何資源(比如數據庫連接)。可在此方法中拋出任何異常,但是此處拋出的任何異常都不會阻止容器的銷毀,只會被記錄到日志中。
來看一下代碼:
1 using System; 2 using System.Collections.Generic; 3 4 namespace CnblogLesson_4_5.Model 5 { 6 /// <summary> 7 /// 人類 8 /// </summary> 9 public class Person : Spring.Objects.Factory.IInitializingObject , System.IDisposable 10 { 11 public Person() { 12 Console.WriteLine("Person 的構造函數"); 13 } 14 15 public void AfterPropertiesSet() 16 { 17 Console.WriteLine("實現Spring.Objects.Factory.IInitializingObject"); 18 } 19 20 21 public void Dispose() 22 { 23 Console.WriteLine("實現System.IDisposable接口"); 24 } 25 } 26 }
運行程序,看一下結果:
1 using System; 2 using Spring.Context; 3 using Spring.Context.Support; 4 using CnblogLesson_4_5.Model; 5 6 namespace CnblogLesson_4_5 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 //通過IApplicationContext來配置 13 IApplicationContext context = ContextRegistry.GetContext(); 14 Person hexu = context.GetObject("hexu") as Person; 15 16 Console.ReadKey(); 17 } 18 } 19 }
可以看到 AfterPropertiesSet() 方法在 Person 類的構造函數之后調用。
注意:
Spring.Net 有一點好處就是,幾乎所有的需求都可以通過配置來完成。一般來說,盡量不要使用 IInitializingObject接口 或 IDisposable接口 。我們可以在XML中配置object 節點的 init-method屬性 或 destroy-method屬性 。配置完成之后,Spring.Net 的 IOC 容器就會幫我們在該對象被實例時調用 init-method 設置的方法 ,在容器被銷毀時調用 destroy-method 設置的方法。
來看一下代碼:
1 using System; 2 3 namespace CnblogLesson_4_5.Model 4 { 5 public class Friend 6 { 7 public Friend() 8 { 9 Console.WriteLine("Friend 的構造函數"); 10 } 11 12 public void Init() 13 { 14 Console.WriteLine("手動配置init-method=Init"); 15 } 16 17 public void Destroy() 18 { 19 Console.WriteLine("手動配置destroy-method=Destroy"); 20 } 21 } 22 }
Objects.xml 配置:
<?xml version="1.0" encoding="utf-8" ?> <objects xmlns="http://www.springframework.net"> <!---手動配置 初始化方法和銷毀方法--> <object id="jeery" type="CnblogLesson_4_5.Model.Friend,CnblogLesson_4_5" init-method="Init" destroy-method="Destroy"> </object> </objects>
Program.cs 文件:
1 using System; 2 using Spring.Context; 3 using Spring.Context.Support; 4 using CnblogLesson_4_5.Model; 5 6 namespace CnblogLesson_4_5 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Console.WriteLine("\n\n\n"); 13 14 //通過IApplicationContext來配置 15 IApplicationContext context = ContextRegistry.GetContext(); 16 17 Person hexu = context.GetObject("hexu") as Person; 18 Friend jeery = context.GetObject("jeery") as Friend; 19 20 Console.ReadKey(); 21 } 22 } 23 }
通過運行程序,可以看到 jeery 對象的 Init 方法被調用:
二 . 抽象對象定義和子對象定義
在 Spring.Net 對象定義中,對象定義可能會包含大量的信息,比如與容器相關的信息(即初始化方法、靜態工廠方法名等)、構造器參數和屬性值等。也許有很多很多的信息是重復的,這個時候如果我們把每一個定義都寫在配置文件中,那么配置文件就會很大啊。
而根據面向對象的方式,有很多重復的工作,我們可以使用繼承來實現。
好在 Spring.Net 中也幫我們提供了類似繼承這樣的機制。但是 Spring.Net 中這里說的繼承和面向對象的繼承是不一樣的,在這里的繼承不是真正的繼承,它類似設計模式中的模板方法模式,子對象定義是指從一個父對象定義中繼承了配置數據的對象定義。子對象定義可以根據需要重寫或添加某些配置的值。使用父對象和子對象的定義方式可能會節省大量的鍵入工作。子對象和父對象實質上並沒有繼承的關系,只是使用了父對象的一些信息。
在 Spring.NET,子對象定義由 ChildObjectDefintion類 處理。但用戶一般不會直接接觸到該類,仍可使用 XML 對象定義進行配置。在 XML 對象定義中,通過 parent屬性 標識一個對象定義是子對象定義,該屬性的值即為父對象定義的名稱。
來看一下示例代碼:
1 using System; 2 using System.Collections.Generic; 3 4 namespace CnblogLesson_4_6.Model 5 { 6 /// <summary> 7 /// Child 類,沒有繼承Parent,它比Parent類多一個Age的屬性 8 /// </summary> 9 public class Child 10 { 11 public string Name { get; set; } 12 public int Age { get; set; } 13 } 14 15 }
1 using System; 2 3 namespace CnblogLesson_4_6.Model 4 { 5 /// <summary> 6 /// Parent類,為一個抽象類,有一個Name屬性 7 /// </summary> 8 public abstract class Parent 9 { 10 public string Name { get; set; } 11 } 12 13 }
我們通過常規方式,來裝載對象時,Child 和 Parent 類都需要裝載 Name 屬性。而Spring提供了一種模板方法的方式來配置對象。
配置文件如:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 4 <!--抽象對象定義和子對象定義--> 5 <object id="parent" type="CnblogLesson_4_6.Model.Parent, CnblogLesson_4_6" abstract="true"> 6 <property name="Name" value="parent"/> 7 </object> 8 9 <object id="child" type="CnblogLesson_4_6.Model.Child, CnblogLesson_4_6" parent="parent"> 10 <property name="Age" value="18"/> 11 </object> 12 13 </objects>
通過 parent 我們可以指定 id="child" 的對象,是使用 id="parent" 對象的配置。
通過運行程序,監視變量,我們來看一下,child 和 parent並沒有繼承關系,只是使用了 parent 的配置而已。
我們可以通過上面的例子看到,沒有在子對象定義中顯式指定的屬性會從父對象定義中繼承值;同時子對象定義也可覆蓋父對象定義的屬性值。這種情況來說,子對象必須與父對象兼容,也就是說,子對象必須能夠接受父對象的屬性值。子對象定義可繼承或覆蓋父對象定義的構造器參數、屬性值及方法,也可以添加新值。如果子對象指定了初始化方法、銷毀方法和/或靜態工廠方法,就會覆蓋父對象定義中的相應配置。
但是還有一些設置,子對象是不會從父對象繼承過來的。比如:depends-on屬性的值、自動裝配模式、依賴檢查模式、singleton和lazy-init屬性的值等。即子對象定義不會從父對象定義繼承這些屬性,舉例來說,若父對象(抽象)定義了depends-on,而子對象未定義,那么子對象就沒有依賴項。
如果父對象定義未指定類型...
比如:
1 <object id="parent"> 2 <property name="Name" value="parent"/> 3 </object>
那么父對象肯定是不能被初始化的,因為它的定義是不完整的。這種定義也被隱式的看作為抽象定義。對象定義也可通過 abstract 屬性顯式指定為抽象定義,該屬性的有效值是 true 或者 false。前面我們對父對象設置過 abstract="true" 屬性,就是一個抽象定義。
一個抽象的定義僅僅是為用做模版,或者說是作為子對象定義的父對象定義而存在。將對象定義聲明為抽象可防止容器創建該對象。在試圖創建實例化或獲取該對象的時候,系統就拋出 ObjectIsAbstractException 異常。而在 Spring 容器內負責預創建對象的 PreInstantiateSingletons 方法會忽略抽象的對象定義。
特別要注意的是: IApplicationContext(應用程序上下文) 接口的實現類,而非簡單的對象工廠。在默認情況下會預先創建所有的 singleton 對象。所以,對應用程序上下文來說,下面一點非常重要(至少對於 singleton對象 來說):如果要定義一個僅用做模版的(父)對象定義,而且在該定義中指定了類型,那么就必須保證將其 abstract屬性 設為true,否則應用程序上下文會嘗試預創建這個對象。
這里寫了太多了,IObjectPostProcessor及擴展類(對象后處理器)就留在下篇寫吧!