依赖注入框架 - 第一部分 - 介绍






4.65/5 (24投票s)
对依赖注入设计模式和框架的介绍,通过一个简单的例子来说明。
背景
最近,在面向对象的编程世界中,关于依赖注入或控制反转的概念被广泛讨论。我决定为那些想用很少的精力理解并有效使用这个概念的人介绍它。
引言
那么,依赖注入到底是什么?简单来说,它是消费者在不预先了解被消费组件的具体信息/来源的情况下消费组件的能力。
现在,你可能会问,这有什么新奇的?我们不是早就有了接口吗?我将通过一个更接近依赖注入设计模式的设计模式的例子来更清楚地解释这一点;那就是对象创建,或者说工厂框架。考虑一个典型的工厂模式,如下图所示。
上面显示的工厂模式与真正期望的面向对象抽象之间存在细微的差别。工厂类实际上知道被消费的类(它实现了消费者最初请求的所需接口)这一事实打破了这种抽象和即插即用的功能。这正是依赖注入框架发挥作用的地方。
动机
依赖注入框架的动机来自于“控制反转”和可扩展性的概念。如今,应用程序正在提供越来越多的扩展点来丰富解决方案的功能。在这种情况下,扩展插件实际上是从最初托管它们的容器那里接管了控制权(因此有了“控制反转”这个术语)。因此,依赖注入使得在设计时可以抽象出依赖关系,并在运行时进行动态绑定。这样,就可以通过动态配置来实例化扩展点,并将它们作为属性或构造函数注入到依赖对象中。一个很好的例子是 Eclipse。
依赖注入框架
在依赖注入框架中,消费者指定了一组它需要满足的依赖关系才能执行操作。在运行时,需要进行配置/声明式工作来指定可以满足一组依赖关系的消费对象。然后,一个装配器/运行时组件可以在代码执行之前执行必要的绑定,或者通过异常通知失败。下图描绘了这种行为。
新一代面向对象语言的支持,如元数据访问能力(如反射和动态代码生成/加载),使得依赖注入框架的实现成为可能。然而,这个想法要古老得多,可以在动态链接库,甚至 COM 中看到。
有许多框架可以辅助依赖注入。我将讨论其中一个主要框架,称为 Spring.NET。Spring.NET 实现了许多经过验证的设计模式,使构建企业应用程序更加容易。但我将只关注 Spring.NET 的依赖注入部分。
使用 Spring.NET 进行依赖注入的简单示例
相信我,即使是 Spring.NET 附带的示例代码也不简单。所以,我决定做一个小的启动示例来演示如何使用 Spring.NET 来解决上面讨论的工厂创建场景。
为了构建这段代码,我使用了 Visual Studio .NET 2008,以及从 这里 获取的 Spring.NET 二进制文件。
然后,我创建了一个名为 *SpringDotNETExample* 的简单控制台项目,并向该项目添加了对 Spring.Core 程序集的引用(该程序集可以在你的 Spring.NET 安装目录的 *bin* 文件夹中找到;对我来说,它是 *c:\Program Files\Spring.NET 1.1.2\bin\net\2.0\debug\Spring.Core.dll*)。
场景非常简单。我们有一个通用的接口 `ISayHello`,它有一个字符串属性 `SayHello`,如下所示:
namespace SpringDotNETExample
{
public interface ISayHello
{
String SayHello { get; }
}
}
然后,我创建了一个实现 `ISayHello` 的类 `WorldSayHello`,如下所示:
namespace SpringDotNETExample
{
public class WorldSayHello : ISayHello
{
public string SayHello
{
get { return "Hello Friend!"; }
}
}
}
在运行时,我希望将我的配置绑定到创建 `WorldSayHello` 作为 `ISayHello` 的具体实现。但是,这种映射对于我的主程序来说将是透明的,它将使用 Spring.NET 装配器来请求已映射对象的实例。对于这个例子,我将假设主程序通过键“`ISayHelloInterface”请求此对象,该键必须与映射相对应。
为了创建映射,我在应用程序配置文件 *app.config* 中创建了一个映射。以下是该文件的内容。我已经加粗了文件中比较重要的部分,其余的只是 Spring.NET 的基本配置,不太重要。
<?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">
<description>An example that demonstrates simple IoC features.</description>
<object name="ISayHelloInterface" type="SpringDotNETExample.WorldSayHello,
SpringDotNETExample"/>
</objects>
</spring>
</configuration>
现在,我的程序只需请求映射到键 `ISayHelloInterface 的对象,就可以获得 `ISayHello 接口的实现。请注意,可以在不重新编译代码的情况下更改映射。这可以提供巨大的版本控制能力。以下是我的主程序:
using System;
using Spring.Context;
using Spring.Context.Support;
namespace SpringDotNETExample
{
class Program
{
static void Main(string[] args)
{
IApplicationContext ctx = ContextRegistry.GetContext();
ISayHello hello = (ISayHello)ctx.GetObject("ISayHelloInterface");
Console.WriteLine(hello.SayHello);
}
}
}
运行程序会产生如下输出 :)
Hello Friend!
Press any key to continue . . .
在我的下一篇文章中,我将讨论依赖注入中更高级的概念,例如构造函数注入。敬请期待!
历史
- 初始文章 - 2008 年 6 月 12 日,星期四。