[Spring.NET IoC] 作者:Q.yuhen 来源:Q.yuhen

[Spring.NET IoC] 之一:基本信息
作者:Q.yuhen  来源:Q.yuhen  
Spring.NET 移植自著名的 Java 开源项目 —— Spring,借助于 .NET 强大的反射机制,甚至拥有比原 Java 版本更强大的功能。只是不知道什么原因,在 .NET 领域似乎没有多少热度,其影响力甚至不如 Castle。因准备在个人项目中使用 IoC,因此花些时间对 Spring.NET 和 Castle 都作一些了解,本文权作学习笔记。
Spring.NET 的宣传口号中有 "non-invasiveness. Quite simply" 的字样,表示是非入侵且易用的,当然作为一个IoC容器,这些都是最基本的特征。
在 Spring.NET IoC 中最核心的内容应该是 IObjectFactory、IApplicationContext、IObjectDefinition 这三个接口了。IObjectFactory 是核心容器接口,负责管理容器内的注入对象,而 IApplicationContext 则是 IObjectFactory 的继承,它扩展了一些功能。IObjectDefinition 是注入对象的定义接口,供 IObjectFactory / IApplicationContext 调用。
大多数时候我们会选择 IApplicationContext 接口的实现类来操控 Spring.NET IoC 容器。
1. Spring.Context.Support.ContextRegistry
将配置信息写入 app.config / web.config 时所使用该类。
2. Spring.Context.Support.XmlApplicationContext
使用独立的 xml 配置文件,或者使用多个 xml 配置文件时,使用该类。
3. Spring.Context.Support.StaticApplicationContext
直接在代码中装配容器时,使用该类。
当然,你还可以使用 Spring.Objects.Factory.Xml.XmlObjectFactory 等直接实现 IObjectFactory 的类型。
作为该篇的结束,写一个简单的 "Hello World" 尝试一下。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class HelloWorld
  {
    public override string ToString()
    {
      return "Hello, World!";
    }
  }
  public class Program
  {
    static void Main(string[] args)
    {
      StaticApplicationContext context = new StaticApplicationContext();
      context.RegisterPrototype("HelloWorld", typeof(HelloWorld), null);
      object o = context.GetObject("HelloWorld");
      Console.WriteLine(o);
    }
  }
}
[Spring.NET IoC] 之二:配置文件
Spring.NET IoC 支持2种配置文件方式:
1. 应用程序配置文件
app.config / web.config


 
   
     

     

   
 
 
   
     
   
    http://www.springframework.net">
     
     

   
 
test.cs
      IApplicationContext context = ContextRegistry.GetContext();
      object o = context.GetObject("HelloWorld");
2. 独立配置文件
springtest.xml

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
 
 

test.cs
IApplicationContext context = new XmlApplicationContext(@"springtest.xml");
object o = context.GetObject("HelloWorld");
建议使用独立的配置文件,应用程序配置文件已经被塞入太多内容了。Spring.NET 还支持很多高级的
[Spring.NET IoC] 之三:获取对象
依照第二篇的配置文件,我们可以初步注入我们所需的类型。本篇将记录获取对象的不同方法。
1. 构造方法创建对象
这种方式最常见,大多数时候我们都会采取此方式获取对象。如果目标对象需要提供构造参数,我们也可以在配置文件中提供。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
   
 


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class HelloWorld
  {
    private string name;
    private int age;
    public HelloWorld(string name, int age)
    {
      this.name = name;
      this.age = age;
    }
    public override string ToString()
    {
      return String.Format("Name={0}; Age={1}", name, age);
    }
  }
  public class Program
  {
    static void Main(string[] args)
    {
      IApplicationContext context = new XmlApplicationContext(@"Config\Spring.xml");
      object o = context.GetObject("HelloWorld");
      Console.WriteLine(o);
    }
  }
}
需要注意的是 Spring.NET IoC 缺省对象创建方式是 "Singleton",我们写个例子验证一下。
object o = context.GetObject("HelloWorld");
object o2 = context.GetObject("HelloWorld");
Console.WriteLine(object.ReferenceEquals(o, o2)); // output: true
我们只需在配置文件中添加一个标记即可改变为非 Singleton 方式。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
   
 

2. 静态工厂方法创建对象
我们提供另外一个 HelloWorld 类,该类型没有公用构造方法,只能通过静态方法创建对象。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class HelloWorld
  {
    private HelloWorld()
    {
    }
    public static HelloWorld Create()
    {
      return new HelloWorld();
    }
  }
  public class Program
  {
    static void Main()
    {
      IApplicationContext context = new XmlApplicationContext(@"Config\Spring.xml");
      object o = context.GetObject("HelloWorld");
      object o2 = context.GetObject("HelloWorld");
      Console.WriteLine(object.ReferenceEquals(o, o2)); // output: true
    }
  }
}
对于这种方式,我们只需指定 "factory-method" 即可,需要注意的是所指定的方法必须是静态方法。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
 

3. 实例工厂方法创建对象
修改上面的例子。在下面的代码中我们必须通过一个名为 Creator 的类才能创建 HelloWorld。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class Creator
  {
    public HelloWorld Create()
    {
      return new HelloWorld();
    }
  }
  public class HelloWorld
  {
    internal HelloWorld()
    {
    }
  }
  public class Program
  {
    static void Main()
    {
      IApplicationContext context = new XmlApplicationContext(@"Config\Spring.xml");
     
      object o = context.GetObject("HelloWorld");
      object o2 = context.GetObject("HelloWorld");
      Console.WriteLine(object.ReferenceEquals(o, o2)); // output: true
    }
  }
}

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
 
通过上面的配置文件我们知道,我们必须提供一个 Creator 的注入声明,同时要为 HelloWorld 指定创建者的 "factory-object" 和 "factory-method"。
4. 对象初始化方法
我们可以在配置文件中使用 "init-method" 指定类型构造方法以外的初始化方法。该方法会在对象构造方法之后被容器调用执行,以便我们完成一些初始化操作。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class HelloWorld
  {
    public HelloWorld()
    {
    }
    public void Init()
    {
      Console.WriteLine("Init...");
    }
  }
  public class Program
  {
    static void Main()
    {
      IApplicationContext context = new XmlApplicationContext(@"Config\Spring.xml");
      object o = context.GetObject("HelloWorld");
      Console.WriteLine(o);
    }
  }
}
需要注意的是对于 Singleton 方式来说,初始化只会被执行一次,因为后续对象是直接从容器中拷贝引用而已。
5. 设置对象属性
除了在配置文件中提供构造参数外,我们还可以直接为对象属性赋值。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class HelloWorld
  {
    public HelloWorld()
    {
    }
    private string name;
    public string Name
    {
      get { return name; }
      set { name = value; }
    }
    public override string ToString()
    {
      return name;
    }
  }
  public class Program
  {
    static void Main()
    {
      IApplicationContext context = new XmlApplicationContext(@"Config\Spring.xml");
     
      object o = context.GetObject("HelloWorld");
      Console.WriteLine(o);
    }
  }
}

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
 

6. 设置类型参数
上面的例子我们注入的数据都是基本类型,对于自定义类型我们需要做些声明。下面的例子中 HelloWorld 需要 2 个构造参数,分别是一个自定义类型和 Type。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Spring.Core;
using Spring.Context;
using Spring.Context.Support;
namespace ConsoleApplication1.SpringNet
{
  public class MyData
  {
    public override string ToString()
    {
      return "MyData...";
    }
  }
  public class HelloWorld
  {
    private MyData data;
    public HelloWorld(MyData data, Type type)
    {
      this.data = data;
      Console.WriteLine(type);
    }
    public override string ToString()
    {
      return data.ToString();
    }
  }
  public class Program
  {
    static void Main()
    {
      IApplicationContext context = new XmlApplicationContext(@"Config\Spring.xml");
      object o = context.GetObject("HelloWorld");
      Console.WriteLine(o);
    }
  }
}

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
   
 
 
一定要注意,注入一个对象和一个 Type 的不同之处。
配置文件功能,将在后面做进一步介绍。
[Spring.NET IoC] 之四:配置补充
 
1. 别名

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
 
我们为 HelloWorld 创建了一个别名 HelloWorld2,我们同样可以通过 HelloWorld2 获取对象。请注意下面的测试代码输出结果。
object o = context.GetObject("HelloWorld");
object o2 = context.GetObject("HelloWorld2");
Console.WriteLine(object.ReferenceEquals(o, o2)); // output: true
2. 继承
下面的例子中,我们为 HelloWorld 添加了一个 "parent" 声明,该申明表示 HelloWorld 继承 test 的配置属性,包括构造参数和属性设置,但不包括 "singleton" 等。当然继承的仅仅是配置信息,而不是类型。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
 

 
 
3. 内联
对于下面的配置文件,我们还可以改成内联方式。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
 
 
修改结果

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
     
   
 
4. 空值
注意空值(null) 和空字符串("")不同。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
     
   

 


[Spring.NET IoC] 之五:列表参数
我们可以在配置文件中向构造方法或者属性注入列表型参数,诸如 Array、ArrayList、Hashtable 等。
1. IList
在 .NET Framework 中实现 IList 的主要是 Array、ArrayList。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
     
        1
        2
        3
        4
     

   

 


public class HelloWorld
{
  public HelloWorld(IList list)
  {
    Console.WriteLine(list); // output: ArrayList
    foreach (object o in list)
      Console.WriteLine(o);
  }
}
我们会发现 Spring.NET IoC 缺省使用 ArrayList 来实现 IList 列表参数。
2. IDictionary
实现 IDictionary 的最常用类型是 Hashtable。

http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net
         http://www.springframework.net/xsd/spring-objects.xsd">
 
   
     
       
       
       
       
     

   

 


public class HelloWorld
{
  public HelloWorld(IDictionary dict)
  {
    Console.WriteLine(dict); // output:System.Collections.Specialized.HybridDictionary
    foreach (object o in dict.Keys)
    {
      Console.WriteLine("{0}={1}", o, dict[o]);
    }
  }
}
看看 System.Collections.Specialized.HybridDictionary 的MSDN说明
在集合较小时,使用 ListDictionary 来实现 IDictionary,然后当集合变大时,切换到 Hashtable。
建议将该类用于字典中的元素数量未知的情况。它利用了 ListDictionary 处理小集合时性能改善的优点,同时也可灵活地切换到处理较大集合时能力比 ListDictionary 更好的 Hashtable。
如果集合的初始大小大于 ListDictionary 的最佳大小,那么集合立即存储在 Hashtable 中,以避免将元素从 ListDictionary 复制到 Hashtable 产生的系统开销。