前面我們知道了什么是對象,什么是對象工廠,什么是應用程序上下文。這一次我們來看一下對象的裝配。
Spring.Net 中有多種裝配對象的方式,裝配這個詞可能比較學術化,我們可以理解為對象的創建。
Spring.Net 中常用的裝配方式有 手動裝配和自動裝配。手動裝配就是根據配置文件然后裝配對象,而自動裝配是指Spring.Net根據指定的自動模式查找相關屬性並自動裝配。這兩種裝配方式之下又分為 通過屬性進行裝配,通過構造器進行裝配,通過靜態工廠裝配,通過實例工廠裝配,泛型的裝配等等。這些裝配方式在下面會一一介紹。
4.4.1 手動裝配
在Spring.Net 中,手動裝配 分為 多種裝配方式,我們在下面介紹常用的幾種裝配方式:
(1) 通過屬性進行裝配
(2) 通過構造器進行裝配
(3) 通過靜態工廠裝配
(4) 通過實例工廠裝配
(5) 對數組的裝配
(6) 對泛型集合的(List,Dictionary)裝配
(7) 泛型對象的裝配
(8) 事件的裝配
4.4.1.1 通過屬性進行裝配
在本小節,我們通過舉例來說明,如何通過屬性來裝配對象,我們這里只討論簡單的屬性,比如數組,集合,自定義類,這些屬性我們放在后面一起討論,這里還是舉常用的Person與Dog的故事。
一個Person類,擁有Id,Name,IsStudent,Dog(小狗)等屬性,睡醒的方法(SleepLightly),還有一個ToString()的方法來輸出信息
1 using System; 2 using System.Collections.Generic; 3 4 namespace CnblogLesson_4_4_1.Model 5 { 6 /// <summary> 7 /// 人類 8 /// </summary> 9 public class Person 10 { 11 public Person() { } 12 public Person(string name) { 13 this.Name = name; 14 } 15 public Person(int id, string name, bool isStudent,Dog dog) { 16 this.Id = id; 17 this.Name = name; 18 this.IsStudent = isStudent; 19 this.Dog = dog; 20 } 21 22 /// <summary> 23 /// 編號 24 /// </summary> 25 public int Id { get; set; } 26 27 /// <summary> 28 /// 人的名稱 29 /// </summary> 30 public string Name { get; set; } 31 32 /// <summary> 33 /// 是否是學生 34 /// </summary> 35 public bool IsStudent { get; set; } 36 37 /// <summary> 38 /// 小狗狗 39 /// </summary> 40 public Dog Dog { get; set; } 41 42 /// <summary> 43 /// 人擁有的物品 44 /// </summary> 45 public Object[] ObjArray { get; set; } 46 47 /// <summary> 48 /// 想要看的書 49 /// </summary> 50 public List<string> Books; 51 52 /// <summary> 53 /// 朋友們 54 /// </summary> 55 public Dictionary<string, string> Friends { get; set; } 56 57 /// <summary> 58 /// 睡醒了 59 /// </summary> 60 /// <param name="args"></param> 61 public void SleepLightly(string args) 62 { 63 Console.WriteLine("{0}叫了,把主人驚醒了", args); 64 } 65 66 /// <summary> 67 /// 重寫ToString方法 68 /// </summary> 69 /// <returns></returns> 70 public override string ToString() 71 { 72 if (Dog == null) 73 { 74 Console.WriteLine("我是{0},我的Id是:{1},我是不是學生:{2},我沒有小狗狗", Name, Id, IsStudent); 75 } 76 else { 77 Console.WriteLine("我是{0},我的Id是:{1},我是不是學生:{2},我的小狗叫:{3}", Name, Id, IsStudent, Dog.Name); 78 } 79 return String.Empty; 80 } 81 } 82 }
小狗擁有一個Name的屬性,一個事件,還有一個叫的方法:
1 namespace CnblogLesson_4_4_1.Model 2 { 3 /// <summary> 4 /// 小狗狗 5 /// </summary> 6 public class Dog 7 { 8 public Dog() { } 9 public Dog(string name) { 10 this.Name = name; 11 } 12 13 /// <summary> 14 /// 小狗狗的名字 15 /// </summary> 16 public string Name { get; set; } 17 18 public event SleepLightly sleepLightly; 19 20 /// <summary> 21 /// 叫 22 /// </summary> 23 public void Cry() 24 { 25 if (sleepLightly != null) 26 { 27 sleepLightly.Invoke("貓"); 28 } 29 } 30 } 31 }
程序主方法:
1 using System; 2 using Spring.Context; 3 using Spring.Context.Support; 4 5 namespace CnblogLesson_4_4_1 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 //通過IApplicationContext來配置 12 IApplicationContext context = ContextRegistry.GetContext(); 13 //4.4.1.1 通過屬性進行裝配 14 Person hexu = (Person)context.GetObject("hexu"); 15 hexu.ToString(); 16 17 Console.ReadKey(); 18 } 19 } 20 }
我們接下來創建一個Object.xml 來配置對象,將Object.xml設置為嵌入式資源,(這里一定要設置為潛入式資源)。
屬性的裝配一般使用 <property name="Id" value="1" /> 標簽來表示,name表示要設置的屬性名,如設置Id,value表示Id屬性的值。
裝配簡單的屬性:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 4 <!--4.4.1.1 通過屬性進行裝配--> 5 6 <!--人類對象--> 7 <object id="hexu" type="CnblogLesson_4_4_1.Model.Person,CnblogLesson_4_4_1"> 8 <!--設置編號--> 9 <property name="Id" value="1"/> 10 <!--設置姓名--> 11 <property name="Name" value="hexu"/> 12 <!--設置是否是學生--> 13 <property name="IsStudent" value="false"/> 14 15 <!--我的寵物為一個對象,這個對象的引用是id為kaqi的狗狗--> 16 <property name="Dog" ref="kaqi"/> 17 </object> 18 19 <!--寵物對象--> 20 <object id="kaqi" type="CnblogLesson_4_4_1.Model.Dog,CnblogLesson_4_4_1"> 21 <property name="Name" value="卡琪"/> 22 </object> 23 </objects>
裝配自定義對象:
標簽 <property name="Dog" ref="kaqi"/> 的屬性ref可以設置這個屬性為一個引用,這個引用為id為kaqi的object標簽。
通過監視變量,我們可以看到,我們已經將屬性裝配成功。
4.4.1.2 通過構造器進行裝配
在上一小節我們討論了如何通過屬性來裝配對象,這一小節我們討論如何通過構造器裝配。這一次,我們對Person類和 Dog 類分別增加了有參數構造函數。
通過構造函數裝配,需要使用到<constructor-arg name="Name" value="hexu"/>標簽。Name表示構造函數參數名稱,value表示該參數所賦的值。
Objects.xml為下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <!--4.4.1.2 通過構造器進行裝配--> 4 5 <!--有一個參數的構造函數--> 6 <object id="hexu_2_1" type="CnblogLesson_4_4_1.Model.Person,CnblogLesson_4_4_1"> 7 <constructor-arg name="Name" value="hexu"/> 8 </object> 9 10 <!--有多個參數的構造函數--> 11 <object id="hexu_2_2" type="CnblogLesson_4_4_1.Model.Person,CnblogLesson_4_4_1"> 12 <constructor-arg name="Id" value="1"/> 13 <constructor-arg name="Name" value="hexu"/> 14 <constructor-arg name="IsStudent" value="true"/> 15 <constructor-arg name="Dog" ref="kaqi_2"/> 16 </object> 17 18 <!--寵物對象_2--> 19 <object id="kaqi_2" type="CnblogLesson_4_4_1.Model.Dog,CnblogLesson_4_4_1"> 20 <property name="Name" value="卡琪"/> 21 </object> 22 </objects>
通過運行時監視變量可以看到,通過構造函數裝載對象已經成功:
4.4.1.3 通過靜態工廠進行裝配
通過靜態工廠來進行裝配,就必須要有工廠對象,我們先來創建一個靜態工廠對象
1 using CnblogLesson_4_4_1.Model; 2 namespace CnblogLesson_4_4_1.Factory 3 { 4 public static class StaticFactory 5 { 6 public static Person CreateInstance() { 7 Dog dog = new Dog("卡琪"); 8 Person person = new Person(1,"hexu",false,dog); 9 return person; 10 } 11 } 12 }
再來看一下Objects.xml:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <!--4.4.1.3 通過靜態工廠進行裝配--> 4 <object name="staticFactory" type="CnblogLesson_4_4_1.Factory.StaticFactory,CnblogLesson_4_4_1" factory-method="CreateInstance"/> 5 </objects>
使用靜態工廠裝配,需要配置一個工廠對象,並且設置靜態工廠創建對象的方法factory-method為類中創建對象的方法。
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
4.4.1.4 通過實例工廠進行裝配
通過實例工廠方法裝載對象與通過靜態工廠方法裝載對象的配置方式類似。此時,實例工廠方法所在的對象必須也要配置在同一容器(或父容器)中。
如果要通過實例工廠方法裝載對象,對象定義就不能包含type屬性,而要用factory-object屬性引用工廠方法所在的對象。注意,該屬性值必須是包含工廠方法的對象的名稱,且該對象必須定義在當前容器或父容器中。工廠方法的方法名則通過factory-method屬性指定。(至於為什么不用type,而要使用factory-object呢?這是Spring.Net他們定義的規則。)這里我們也像通過靜態工廠進行裝配一樣,通過實例工廠裝配,需要定義一個實例工廠對象。
實例工廠類:
1 using CnblogLesson_4_4_1.Model; 2 namespace CnblogLesson_4_4_1.Factory 3 { 4 public class InstanceFactory 5 { 6 public Person CreateInstance() { 7 Dog dog = new Dog("卡琪"); 8 Person person = new Person(1,"hexu",false,dog); 9 return person; 10 } 11 } 12 }
Objects.xml 配置如下:
使用實例工廠裝配,需要配置一個工廠對象。如:Id=”instanceFactory”
然后還需要一個實例對象id=” instancePerson”,並設置該實例是通過工廠對象創建,設置factory-object="instanceFactory",還要設置工廠創建實例的方法factory-method="CreateInstance"。
再來看一下Objects.xml:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 4 <!--4.4.1.4 通過實例工廠進行裝配--> 5 6 <!--工廠--> 7 <object id="instanceFactory" type="CnblogLesson_4_4_1.Factory.InstanceFactory, CnblogLesson_4_4_1" /> 8 <!--創建的對象,factory-object所指向的是instanceFactory,表示通過instanceFactory工廠的CreateInstance方法來創建此對象--> 9 <object id="instancePerson" factory-method="CreateInstance" factory-object="instanceFactory" /> 10 11 </objects>
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
4.4.1.5 對數組的裝配
前面我們一起討論過屬性的裝配,但是前面我們討論的都是一些簡單的屬性。這一節我們一起來討論數組如何裝配。
Objects.xml 配置如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <!--4.4.1.5 對數組的裝配--> 4 <!--人類對象--> 5 <object id="hexu_2_5" type="CnblogLesson_4_4_1.Model.Person,CnblogLesson_4_4_1"> 6 <!--設置姓名--> 7 <property name="Name" value="hexu"/> 8 <property name="ObjArray"> 9 <set> 10 <value>親人</value> 11 <value>朋友</value> 12 <value>工作</value> 13 <value>程序</value> 14 </set> 15 </property> 16 </object> 17 </objects>
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
4.4.1.6 對泛型的裝配之List
這一小節,我們一起來討論對List集合的裝載。
Objects.xml 配置如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 4 <!--4.4.1.6 對泛型的裝配之List集合--> 5 <!--人類對象--> 6 <object id="hexu_2_6" type="CnblogLesson_4_4_1.Model.Person,CnblogLesson_4_4_1"> 7 <!--設置姓名--> 8 <property name="Name" value="hexu"/> 9 <property name="Books"> 10 <!--設置集合的類型--> 11 <list element-type="string"> 12 <value>重構</value> 13 <value>WCF全面解析</value> 14 <value>設計模式:可復用面向對象軟件的基礎</value> 15 </list> 16 </property> 17 </object> 18 19 </objects>
標簽<list element-type="string">....</list>表示C#中的List<string>,value 表示 元素的值
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
4.4.1.7 對泛型的裝配之Dictionary集合
這一小節,我們一起來討論對Dictionary集合的裝載。
Objects.xml 配置如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 4 <!--4.4.1.7 對泛型的裝配之Dictionary集合--> 5 6 <!--人類對象--> 7 <object id="hexu_2_7" type="CnblogLesson_4_4_1.Model.Person,CnblogLesson_4_4_1"> 8 <!--設置姓名--> 9 <property name="Name" value="hexu"/> 10 <property name="Friends"> 11 <!--設置集合的類型--> 12 <dictionary key-type="string" value-type="string"> 13 <entry key="ZG" value="張哥"/> 14 <entry key="LG" value="李哥"/> 15 <entry key="WG" value="王哥"/> 16 </dictionary> 17 </property> 18 </object> 19 20 </objects>
標簽<dictionary key-type="string" value-type="string">....</dictionary>表示C#中的Dictionary<string,string>,key-type 表示 鍵的類型,value-type 表示 值的類型。entry則表示每個元素。
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
4.4.1.8 泛型對象的裝配
Spring.Net 中對泛型對象的創建方法和普通對象是一樣的。但有一個很細微的差別。
在為泛型類對象指定type屬性的時候要注意:
第一, 左尖括號<要替換成字符串“<”,因為在XML中左尖括號會被認為是小於號。可讀性來講,我們都知道這並不是理想的方式。
第二,type參數值中不能包含程序集的名稱,因為程序集名稱要求和類型全名用逗號隔開,而在這里逗號已經被用來分隔泛型類的類型參數了。將來可能會用其它字符代替這兩個符號,但目前還沒找到更具可讀性的方案。若要提高可讀性,建議使用類型別名。
先來看一下GenericClass.cs的定義:
1 namespace CnblogLesson_4_4_1.Generic 2 { 3 public class GenericClass<T> 4 { 5 public T obj { get; set; } 6 } 7 }
Objects.xml 配置如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 4 <!-- 4.4.1.8 泛型類的裝配 如: GenericClass<string> --> 5 <object id="hexu_2_8" type="CnblogLesson_4_4_1.Generic.GenericClass<string>, CnblogLesson_4_4_1" > 6 <property name="obj" value="generic"/> 7 </object> 8 9 </objects>
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
5.4.1.9 事件的裝配
Spring.NET的IoC框架中,還提供事件的注入。通過事件的注入,可以使架構體系的耦合降到最低。仍然使用一個例子:主人正在睡覺,小偷來了,然后小狗發現小偷就汪汪叫,主人被小狗的叫聲驚醒。
創建一個 驚醒的委托:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace CnblogLesson_4_4_1.Model 7 { 8 public delegate void SleepLightly(string args); 9 }
配置Objects.xml 文件:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <!--4.4.1.9事件注入--> 4 5 <!--先裝載小狗對象--> 6 <object id="observer_dog" type="CnblogLesson_4_4_1.Model.Dog, CnblogLesson_4_4_1" /> 7 <!--裝載主人對象,主人需要監聽小狗對 驚醒事件的觸發,當小狗叫,主人調用睡醒的方法--> 8 <object id="observer_person" type="CnblogLesson_4_4_1.Model.Person, CnblogLesson_4_4_1"> 9 <!--使用到listener監聽器,ref表示主人要監聽的小狗,event表示主人監聽小狗的哪個事件,method表示,當監聽到的事件觸發時,調用自己的睡醒事件--> 10 <listener event="sleepLightly" method="SleepLightly"> 11 <ref object="observer_dog"/> 12 </listener> 13 </object> 14 15 </objects>
裝配事件需要使用到<listener event="sleepLightly" method="SleepLightly">...</listener>標簽,由於在Dog.cs 和 Person.cs 中都已經寫好了 代碼,現在只需要通過Objects.xml來動態設置參數。<listener event="sleepLightly" method="SleepLightly">...</listener>標簽中,event就是用來指定,需要設置Dog.cs類中的哪個事件(上面是設置sleepLightly事件),method表示,這個事件的值是哪個方法。使用<ref object="observer_dog"/>表示,這個事件的參數。您看,在Dog.cs 類中,我傳了一個字符串“貓”作為參數,最后輸出結果為“貓叫了,把主人驚醒了”!
通過運行時監視變量,通過靜態工廠裝載對象已經成功:
到目前為止,Spring.Net 中大部分手動裝配對象方式 都在上面的文章里了,在下面的一章里,會談到不常用的自動裝配方式。