[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
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>

  <spring>
    <context>
      <resource uri="config://spring/objects"/>
    </context>

    <objects xmlns="http://www.springframework.net">
      <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" >
      </object>
    </objects>

  </spring>
</configuration>

test.cs
      IApplicationContext context = ContextRegistry.GetContext();
      object o = context.GetObject("HelloWorld");

2. 独立配置文件

springtest.xml
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">
  
  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
  </object>
</objects>

test.cs
IApplicationContext context = new XmlApplicationContext(@"springtest.xml");
object o = context.GetObject("HelloWorld");

建议使用独立的配置文件,应用程序配置文件已经被塞入太多内容了。Spring.NET 还支持很多高级的

[Spring.NET IoC] 之三:获取对象 

依照第二篇的配置文件,我们可以初步注入我们所需的类型。本篇将记录获取对象的不同方法。

1. 构造方法创建对象

这种方式最常见,大多数时候我们都会采取此方式获取对象。如果目标对象需要提供构造参数,我们也可以在配置文件中提供。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="name" value="Tom" />
    <constructor-arg name="age" value="234" />
  </object>
</objects>
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 方式。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" singleton="false">
    <constructor-arg name="name" value="Tom" />
    <constructor-arg name="age" value="234" />
  </object>
</objects>

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" 即可,需要注意的是所指定的方法必须是静态方法。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" factory-method="Create">
  </object>
</objects>

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
    }
  }
}
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" factory-method="Create" factory-object="Creator" />
  <object id="Creator" type="ConsoleApplication1.SpringNet.Creator, Learn.CUI" />
</objects>

通过上面的配置文件我们知道,我们必须提供一个 Creator 的注入声明,同时要为 HelloWorld 指定创建者的 "factory-object" 和 "factory-method"。

4. 对象初始化方法

我们可以在配置文件中使用 "init-method" 指定类型构造方法以外的初始化方法。该方法会在对象构造方法之后被容器调用执行,以便我们完成一些初始化操作。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" init-method="Init" />
</objects>
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);
    }
  }
}
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <property name="Name" value="Tom" />
  </object>
</objects>

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);
    }
  }
}
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="data" ref="MyData" />
    <constructor-arg name="type" value="ConsoleApplication1.SpringNet.MyData, Learn.CUI" />
  </object>

  <object id="MyData" type="ConsoleApplication1.SpringNet.MyData, Learn.CUI" />
</objects>

一定要注意,注入一个对象和一个 Type 的不同之处。

配置文件功能,将在后面做进一步介绍。

[Spring.NET IoC] 之四:配置补充

 
1. 别名
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" />
  <alias alias="HelloWorld2" name="HelloWorld"/>
</objects>

我们为 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" 等。当然继承的仅仅是配置信息,而不是类型。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="test" type="ConsoleApplication1.SpringNet.Test, Learn.CUI" singleton="false">
    <constructor-arg name="s" value="abc..." />
  </object>
  
  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI" parent="test" />
</objects>

3. 内联

对于下面的配置文件,我们还可以改成内联方式。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="data" ref="MyData" />
  </object>

  <object id="MyData" type="ConsoleApplication1.SpringNet.MyData, Learn.CUI" />
</objects>

修改结果
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="data">
      <object type="ConsoleApplication1.SpringNet.MyData, Learn.CUI" />
    </constructor-arg>
  </object>

</objects>

4. 空值

注意空值(null) 和空字符串("")不同。
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="s">
      <null/>
    </constructor-arg>
  </object>
</objects>

[Spring.NET IoC] 之五:列表参数 

我们可以在配置文件中向构造方法或者属性注入列表型参数,诸如 Array、ArrayList、Hashtable 等。

1. IList

在 .NET Framework 中实现 IList 的主要是 Array、ArrayList。

<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="list">
      <list>
        <value>1</value>
        <value>2</value>
        <value>3</value>
        <value>4</value>
      </list>
    </constructor-arg>
  </object>
</objects>
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。

<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="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">

  <object id="HelloWorld" type="ConsoleApplication1.SpringNet.HelloWorld, Learn.CUI">
    <constructor-arg name="dict">
      <dictionary>
        <entry key="1" value="a" />
        <entry key="2" value="b" />
        <entry key="3" value="c" />
        <entry key="4" value="d" />
      </dictionary>
    </constructor-arg>
  </object>
</objects>
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 产生的系统开销。

posted on 2009-06-09 13:21  胜者为王  阅读(2067)  评论(0编辑  收藏  举报