第四章 Spring.Net 如何管理您的类___对象的手动装配
前面我们知道了什么是对象,什么是对象工厂,什么是应用程序上下文。这一次我们来看一下对象的装配。
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 中大部分手动装配对象方式 都在上面的文章里了,在下面的一章里,会谈到不常用的自动装配方式。