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 中大部分手动装配对象方式 都在上面的文章里了,在下面的一章里,会谈到不常用的自动装配方式。
示例代码 : http://download.csdn.net/detail/cilence6788/5086311
第四章 Spring.Net 如何管理您的类___对象、对象工厂和应用程序上下文
在前面一章我们介绍了依赖注入,控制反转的概念,以及自己动手搭建了一下Spring.Net的环境。通过这些操作,我们知道了Spring.Net 的核心是使用依赖注入或控制反转这种思想来管理业务对象,降低程序的依赖耦合。这一次的笔记,就是来了解一下,Spring.Net 如何管理我们的类。
4.1 对象,对象工厂和应用程序上下文介绍
在前面的章节,我们曾稍微提到过关于对象,对象工厂和应用程序上下文。我们也知道了可以用这些接口来管理对象,接下来会详细介绍他们。
Spring.Core 程序集是Spring.Net的IOC容器管理对象的基础,在Spring.Core程序集中提供了IObjectFactory,IApplicationContext和IObjectDefinition接口来帮助我们进行对象管理。
IObjectFactory接口为Spring.Net 提供了一种高级的配置机制,可用Xml,txt等等很多类型的文件来任意配置对象的信息。
IApplicationContext接口则扩展了IObjectFactory,并增加了面向切面编程(AOP)和消息资源处理等等的功能。
简单的说,IObjectFactory接口提供了配置框架的基本功能,IApplicationContext接口又在其基础上扩展了许多企业级功能。可以说IApplicationContext就是IObjectFactory的超集,它具备了IObjectFactory所有的功能和行为。
4.2 IObjectFactory和IApplicationContext对比
IObjectFactory 是初始化,配置和管理对象的真正容器,实际上所有的扩展容器都是实现了IObjectFactory接口,比如我们前面提到过的Spring.Objects.Factory.Xml.XmlObjectFactory类也是实现了IObjectFactory接口的。各个对象之间常常都会相互协作,也就是说他们相互具有依赖性。这些依赖关系我们可以通过IObjectFactory的配置反应出来。(注:有些配置是看不到的,比如运行时期对象之间方法的调用等。)
(上图是:IObjectFactory的定义)
(上图是:IApplicationContext的定义)
可以看到,Spring.Context.IApplicationContext是实现了IObjectFactory接口的,IObjectFactory的派生类有很多很多,这里最常用的只有几个,不常用的这里就不一一介绍了。如有感兴趣的朋友可以详细了解。
(上图是 IObjectFactory的派生接口或者派生类)
我们前面提到IApplicationContext接口是IObjectFactory的超集,在实际开发中,我一般都会使用IApplicationContext来作为Spring.Net 依赖注入的容器。
4.3 对象的定义
前面我们一直提Spring.Net 管理对象,却一直没有说过如何定义对象。在Spring.Net 容器中我们常常使用Xml文件来定义对象,虽然txt等等的文件类型也可以定义对象。但在这里我只记录了常用的Xml文件来定义对象。如CnblogLesson_3_3中的Object.xml 就是我们用来定义对象的Xml文件。
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <object id="readerDal" type="CnblogLesson_3_3.ReaderDAL,CnblogLesson_3_3"> 4 </object> 5 </objects>
所有对象的父节点都是objects标签,xmlns属性表示它是属于Spring.Net 的配置文件(这个属性必须给上,否则Spring.Net的解析器不认识),objects标签下面可以有一个或者多个object标签。
Id表示这个对象的编号,我们通过这个id就可以获取到这个对象。如:
1 //通过IApplicationContext来配置 2 IApplicationContext context = ContextRegistry.GetContext(); 3 dal = (IReaderDAL)context.GetObject("readerDal");
type表示这个对象的类型,一般用type=”类的全名称,类的命名空间”来表示。
如:type="CnblogLesson_3_3.ReaderDAL,CnblogLesson_3_3"
其中他们的属性有很多,在这小节只介绍两个(id,type),剩下的属性在下面的章节会一一为大家介绍。
本节笔记可能写得有点啰嗦,但是只有一个简单目的,就是让人理解到Spring.Net 容器(包括IApplicationContext,IObjectFactory)与对象之间的作用,以及它们三者之间究竟有着何种关系。