这是 Unity 1.0 文档翻译的第一部分,欢迎大家参与翻译和审校,详情请看
http://forum.entlib.net.cn/showtopic-235.aspx
Unity 应用程序块(Unity)是一个轻量级、可扩展的依赖注入容器,支持构造函数、属性和方法调用注入。它有下列优点:
- 简化了对象的创建,尤其是分层的对象结构和依赖,并简化了应用程序代码。
- 支持需求的抽象,这允许开发人员在运行时或者配置中指定依赖,并简化了横切关注点的管理。
- 通过延迟将配置组装到容器来提高灵活性。
- 服务定位功能允许客户代码保存或者缓存容器。这在开发人员可以持久化容器到 ASP.NET Session 或者 Application 中的 ASP.NET Web 应用程序中特别有用。
本主题包含了一系列有用的章节,它们提供了有助于决定 Unity 应用程序块是否满足需求的信息。本主题的章节如下:
- 常见场景
- 示例应用程序代码
- Unity 应用程序块的亮点
- 确定何时使用 Unity 应用程序块
除了此介绍材料以外,文档还包含了下列主题:
更多信息
相关信息,请参见以下指南:
常见场景
Unity 应用程序块解决忙于基于组件的软件工程的开发人员所面对的问题。现代业务系统除了单独解决横切关注点如日志、认证、授权、缓存和异常处理的组件以外,还由定制的业务对象和在应用程序中完成特殊的或者一般的任务的组件组成。
成功构建这样的应用程序的关键是获得解耦的或者极度松耦合的设计。松耦合的应用程序更加灵活、更加易于维护。同时易于在开发期间进行测试。可以模拟强实质依赖的对象的桩(轻量级模拟的实现)。例如,数据库连接、网络连接、ERP 连接和富用户接口组件。
依赖注入是一种用于构建松耦合应用程序的主要技术。它提供了处理对象间依赖的方法。例如,一个处理用户信息的对象可能依赖于访问数据存储、验证信息和检查用户是否被授权执行更新的其他对象。依赖注入技术可以确保用户类正确的初始化及组装所有这些对象,特别是依赖可被抽象的地方。
下列设计模式定义了简化处理的架构和开发方法:
- 控制反转 (IoC) 模式,这是一种一般的模式,它描述了用于支持对象可以“查找”它们需要的其他对象的实例的插件架构的技术。
- 依赖注入 (DI) 模式,这是 Ioc 模式的一种特殊情况,是一种基于改变对象的行为而不改变类的内部的接口编程技术。开发人员编写实现接口的类代码,并基于接口或者对象类型使用容器注入依赖 的对象实例到类中。用于注入对象实例的技术是接口注入、构造函数注入、属性(设置器)注入和方法调用注入。
此指南中的场景包含了下列场景以示范可以使用 Unity 应用程序块的方法:
另外,
Unity 快速入门示范了许多这些技术,包括一个 MVC 模式的简单实现和一个做为自定义容器扩展的 Event Broker 服务的实现。
示例应用代码
通过使用依赖注入框架和控制反转机制,开发人员可以生成并组成可以包含依赖对象实例和设置的自定义类和对象。Unity 应用程序块支持这种功能,允许开发人员使用如配置的容器注入、构造函数注入、属性注入和方法调用注入这些技术来生成和组合对象的实例和所有依赖的对象和设置。
Unity 应用程序块暴露了二个用容器注册类型和映射的方法:
- RegisterType。此方法 用容器注册一个类型。在适当的时候,容器将构建一个指定的类型的实例。这可以通过类的特性或者调用 Resolve 方法时初始化依赖注入。构建的对象的生命周期与在方法参数中指定的生命周期一致。如果没有指定生命周期的值,将为类型注册一个暂时的生命周期,这意味着容 器将为每一个对 Resolve 的调用创建一个新的实例。
- RegisterInstance。此方法将一个指定的类型的已存在的实例注册到容器,并带有指定的生命周期。容器将在生命周期内返回已有的实例。如果没有指定生命周期,实例将拥有容器控制的生命周期。
类型的配置容器注册
做为 RegisterType 和 Resolve 方法的重载的一个示例,下列代码注册了一个用于接口 IMyService 的映射,将指定容器将返回 CustomerService 类(实现了IMyService 接口)的实例。
C#
IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType<IMyService, CustomerService>();
IMyService myServiceInstance = myContainer.Resolve<IMyService>();
Visual Basic
Dim myContainer As IUnityContainer = New UnityContainer()
myContainer.RegisterType(Of IMyService, CustomerService)()
Dim myServiceInstance As IMyService = myContainer.Resolve(Of IMyService)()
已有对象实例的配置容器注册
做为使用 RegisterInstance 和 Resolve 方法重载的一个示例,下列代码注册了实现了 IMyService 接口的 LoggingService 类的一个已有实例,然后获取此实例:
C#
IUnityContainer myContainer = new UnityContainer();
LoggingService myExistingObject = new LoggingService();
myContainer.RegisterInstance<IMyService>(myExistingObject);
IMyService myServiceInstance = myContainer.Resolve<IMyService>();
Visual Basic
Dim myContainer As IUnityContainer = New UnityContainer()
Dim myExistingObject As New LoggingService()
myContainer.RegisterInstance(Of IMyService)(myExistingObject)
Dim myServiceInstance As IMyService = myContainer.Resolve(Of IMyService)()
构造函数注入
做为构造函数注入的一个示例,如果开发人员用 Unity 容器的 Resolve 方法实例化的类有一个构造函数定义了一个或多个对其他类的依赖,Unity 容器将自动创建指定在构造函数参数中的依赖的对象实例。例如,下列代码展示了依赖于 LoggingService 对象的 CustomerService 类。
C#
public class CustomerService
{
public CustomerService(LoggingService myServiceInstance)
{
// work with the dependent instance
myServiceInstance.WriteToLog("SomeValue");
}
}
Visual Basic
Public Class CustomerService
Public Sub New(myServiceInstance As LoggingService)
' work with the dependent instance
myServiceInstance.WriteToLog("SomeValue")
End Sub
End Class
在运行时,开发人员使用容器的 Resolve 方法创建了 CustomerService 类的一个实例,这导致了实例生成框架将具体类 LoggingService 的一个实例注入到 CustomerService 类中。
C#
IUnityContainer uContainer = new UnityContainer();
CustomerService myInstance = uContainer.Resolve<CustomerService>();
Visual Basic
Dim uContainer As IUnityContainer = New UnityContainer()
Dim myInstance As CustomerService = uContainer.Resolve(Of CustomerService)()
属性(设置器)注入
除了前面描述的构造函数注入以外,Unity 应用程序块还支持属性和方法调用注入。下列代码示范了属性注入。ProductService 类暴露了一个引用另一个名为 SupplierData 类(没有定义在下列代码中)的实例的属性。要强制依赖对象的依赖注入,开发人员必须像下列代码中一样使用 Dependency 特性装饰属性的声明:
C#
public class ProductService
{
private SupplierData supplier;
[Dependency]
public SupplierData SupplierDetails
{
get { return supplier; }
set { supplier = value; }
}
}
Visual Basic .NET
Public Class ProductService
Private supplier As SupplierData
<Dependency()> _
Public Property SupplierDetails() As SupplierData
Get
Return supplier
End Get
Set (ByVal value As SupplierData)
supplier = value
End Set
End Property
End Class
现在,使用 Unity 应用程序块创建 ProductService 类的一个实例将自动生成 SupplierData 类的一个实例,并将它设置为 ProductService 类的 SupplierDetails 属性的值。
注意:关于在应用程序中如何使用这些和其他 Unity 应用程序块的特性的更多细节,请参见
关键场景。
Unity 应用程序块的亮点
Unity 应用程序块包含了下列特性:
- 它提供了用于构建(或者组合)对象实例的机制,它可能包含其他依赖的对象实例。
- 它暴露了支持使用映射和对象(包括单件(singleton)实例)配置容器的 RegisterType 方法,以及返回可以包含任何依赖的对象的构建对象实例的 Resolve 方法。
- 通过允许注入预配置的对象到由程序块构建的类中来提供控制反转(IoC)功能。开发人员可以在构建函数中指定接口或者类的类型(构造函数注入),或者应用属性和方法特性来初始化属性注入和方法调用注入。
- 支持容器的继承层次。容器可能有子容器,允许将对象定位查询从子容器传递到父容器中。
- 可以从标准配置系统如 XML 文件中读取配置信息,并使用它来配置容器。
- 不需要对象类的定义。不需要应用特性到类上(使用属性或者方法调用注入除外),以及无限制的类声明。
- 支持开发人员可以实现的自定义容器扩展。例如,允许其他对象构建方法和容器特性,如缓存。
决定何时使用 Unity 应用程序块
依赖注入提供了简化代码、抽象对象间依赖以及自动生成依赖对象实例的机会。然而,处理可能会对性能有少量的影响,并且在仅仅简化已有的依赖时会增加复杂度。
通常,可以在下列时机使用 Unity 应用程序块:
- 对象和类可能依赖于其他对象和类时。
- 依赖很复杂或者需要抽象。
- 要利用构造函数、方法或者属性调用注入特性。
- 要管理对象实例的生命周期。
- 要在运行时配置和改变依赖。
- 要在 Web 应用程序中跨越回发缓存或者持久化依赖。
在下列时机不使用 Unity 应用程序块:
- 对象和类不依赖于其他对象或者类。
- 依赖非常简单并且不需要抽象