1、IoC/DI简介

IoC 即 Inversion of Control,DI 即 Dependency Injection,前一个中文含义为控制反转,后一个译为依赖注入,可以理解成一种编程模式,详细的说明可参见大牛Martin Fowler的强文 http://martinfowler.com/articles/injection.html,借用Hollywood的名言:Don't call us, we'll call you,意即你呆着别动,到时我会找你。控制反转的核心是控制权的转移,从原有的应用程序转移到框架如IoC容器,从而实现模块间的解耦。

2、Unity是什么?

Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入(Constructor Injection)、属性注入(Property Injection),以及方法调用注入(Method Call Injection)。现在Unity最新的版本的3.5版,可以在微软的开源站点:https://github.com/unitycontainer/unity下载最新的发布版本和文档。

它有助于构建松耦合的应用程序和为开发者提供以下便利:

  • 简化对象的创建,特别在分层对象结构和依赖的情形下;
  • 它支持需求的抽象化,这允许开发人员在运行时或在配置文件中指定依赖,简化横切关注点(crosscutting concerns)的管理;
  • 它通过把组件配置推给容器来决定,增加了灵活性;
  • 服务定位能力; 这使客户端能够存储或缓存容器;
  • 轻松构建松耦合结构的程序,从而让整个程序框架变得清晰和易于维护。

3、如何使用Unity?

下面我们用一个简单的例子来演示如何使用Ioc框架:Unity。我们的大多数应用程序都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。使用Unity就是要解决这个这个问题。

Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中将具体依赖注入划分为三种形式,即构造器注入、属性(设置)注入和接口注入,习惯将其划分为一种(类型)匹配和三种注入:

  • 类型匹配(Type Matching):虽然我们通过接口(或者抽象类)来进行服务调用,但是服务本身还是实现在某个具体的服务类型中,这就需要某个类型注册机制来解决服务接口和服务实现类型之间的匹配关系;
  • 构造器注入(Constructor Injection):IoC容器会智能选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
  • 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性;
  • 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法。

依赖翻转的核心原则:

1、高层模块不应该依赖底层模块,两个都应该依赖抽象(抽象类或接口);

2、抽象不应该依赖细节,细节应该依赖抽象。

下面我采用“属性注入(Property Injection)”的方式演示如何使用Unity框架。

3.1、定义接口类

一个简单日志记录接口类,用于记录请求的报文。

    public interface ILogger
{
Tuple<string, string> GetLogContent(AtomRequest request, AtomResponse response = null);
void Log(Tuple<string, string> logInfo);
}

3.2、定义接口的实现类

定义2个接口的实现类,分别用来实现对请求的响应的报文进行日志记录。

internal class LogRequest : ILogger
{
public Tuple<string, string> GetLogContent(AtomRequest request, AtomResponse response = null)
{
var reqType = request.GetType().Name;
switch (reqType)
{
case "AtomQueryRequest":
{
var req = (AtomQueryRequest) request;
return Tuple.Create(request.CollectionId, string.Format("流水号:{0} 交易类型:{1}", req.TransNo, req.TransType));
}
case "AtomSaleRequest":
{
var req = (AtomSaleRequest) request;
return Tuple.Create(req.CollectionId, string.Format("流水号:{0} 订单号:{1} 金额:{2}", req.TransNo, req.OrderNo, req.LocalAmount));
}
default:
{
throw new CheckRequestException("无效的交易类型:".Contact(reqType));
}
}
} public void Log(Tuple<string, string> logInfo)
{
LogManager.InfoRequest(logInfo.Item1, logInfo.Item2);
}
}

3.3、定义容器并注册接口及接口实现类之间的映射关系

项目引用:Microsoft.Practices.Unity.dll

internal class ServiceContainer
{
// 核心容器类型的定义,设置为静态的,公开的,可为其它任何类型调用。
public static UnityContainer RtpContainer; // 初始化容器并在容器中注册项目程序中所有的依赖关系
static ServiceContainer()
{
RtpContainer = new UnityContainer(); // 每次调用,容器都会生成一个新的对象实例
RtpContainer.RegisterType<IResponseProcessor, ResponseProcessor>(); // 注册为单例,任何时候调用都使用同一个对象实例
RtpContainer.RegisterType<ILogger, LogRequest>("Request", new ContainerControlledLifetimeManager());
RtpContainer.RegisterType<ILogger, LogResponse>("Response", new ContainerControlledLifetimeManager());
}
}

什么时候注册为单例,我的个人标准为:实现类,比如:LogRequest类,没有静态成员或者静态成员没有并发写的可能都可以用,使用单例可以减少频繁创建对象可能造成的开销。在注册类型时,如果要注册为单例模式,额外传入一个:new ContainerControlledLifetimeManager() 参数即可,表示创建对象的生命周期由容器来控制。另外如果一个接口由多个实现类,如上面的LogRequest和LogResponse都实现了ILogger接口。这样在注册map映射关系时,需要额外使用一个name参数(比如上面的“Request”,“Response”)来唯一标识map关系。使用XML也可以实现依赖关系的注册,但我更倾向于使用:约定优于配置(convention over configuration)的原则,也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。

3.4、依赖注入

internal abstract class AbstractRequest
{
static AbstractRequest()
{
JsConfig.EmitCamelCaseNames = true;
JsConfig.IncludeNullValues = true;
} [Dependency("Request")]
public ILogger Logger { get; set
; } [Dependency]
public IRequestCheck RequestCheck { get; set; } protected virtual void CheckRequest(AtomRequest request)
{
if (null == request)
{
throw new ArgumentNullException("request", "请求实体不能为NULL");
}
}
}

上面的代码我采用了“属性注入(Property Injection)”的方式注入了一个Logger属性,并且Dependency属性类的name参数为:Request,标识当AbstractRequest的实现类被实例化时,IoC容器自动初始化该Logger属性为一个LogRequest对象实例。

3.5、使用注入的属性

internal class ProcessAtomSaleRequest : AbstractRequest, IAtomRequest
{
[Dependency("AtomSale")]
public MessageProviderFactory MessageProviderFactory { get; set; } public object Execute(AtomRequest request)
{
CheckRequest(request); // 使用AbstractRequest类中注入的属性Logger,ProcessAtomSaleRequest被实例化时Logger属性自动被初始化为LogRequest实例对象
Logger.Log(Logger.GetLogContent(request));
}
}

3.6、使用容器来解析并创建对象实例

public class AtomSale : AtomTransaction
{
protected override object ProcessRequest(AtomRequest request)
{
return ((IAtomRequest)ServiceContainer.RtpContainer.Resolve(typeof(IAtomRequest), "AtomSale")).Execute(request);
}
}

ServiceContainer.RtpContainer就是我们前面定义的静态的、公共的容器类(类型为:UnityContainer),在第一次被调用时初始化。Resolve方法通过指定抽象类型及对应的name属性来确定唯一映射关系并创建对象,最后执行对象的Execute方法。

 总结:

使用IoC框架后,使用相同架构模型的应用,其高层抽象可以完全移植,只须关注实现类的业务细节即可,业务逻辑修改时也只须改动实现的部分,这样就实现了抽象层和实现层的分离,同样对象创建职责也做了转移。系统解耦或松耦合也就顺理成章了。如果所有代码都揉合在一起,任何代码的修改都可能对其它代码造成影响,如果没能细致和全面的回归测试,线上故障也难免发生。

帮助到您了吗?

打赏作者(支付宝):

使用Microsoft的IoC框架:Unity来对.NET应用进行解耦的更多相关文章

  1. .NET MVC4 实训记录之一(引入Unity3.0 Ioc框架)

    一直在做维护项目,没有机会接触完整的架构,于是自学.NET MVC.自今日起,将自学的过程.遇到的问题以及解决方案记录下来. 在WebApp项目中右键,使用NuGet引入Unity3.0.

  2. .Net常见的IOC框架及AOP框架

    IOC框架 Unity:微软patterns&practicest团队开发的IOC依赖注入框架,支持AOP横切关注点. MEF(Managed Extensibility Framework) ...

  3. .Net IOC 框架

    CastleWindsor 参见:CastleWindsor | .Net IOC 框架 AutoFace 参见:AutoFace | .Net IOC 框架 Unity 参见:Unity | .Ne ...

  4. .Net IOC框架入门之一 Unity

    一.概述 IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...

  5. .Net IOC框架入门之——Unity

    一.概述 IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...

  6. 依赖反转Ioc和unity,autofac,castle框架教程及比较

    1.依赖倒置的相关概念 http://www.cnblogs.com/fuchongjundream/p/3873073.html IoC模式(依赖.依赖倒置.依赖注入.控制反转) 2.依赖倒置的方式 ...

  7. MVC4 基于 Unity Ioc 框架的 ControllerFactory

    首先引入Untiy框架. 可以在NuGet程序包 管理器中直接安装. 新建 继承DefaultControllerFactory  的UnityControllerFactory: 重写虚方法GetC ...

  8. 今天研究Unity Ioc 框架

    今天研究Unity Ioc 框架,被自己坑了两个多小时. 运行就报错,反反复复检查了很多次,配置文件,代码都没有问题,也从新写了好几遍. 最后仔细看报错消息才知道,config文件没有生成到目录……… ...

  9. ASP.NET MVC IOC之Unity攻略

    ASP.NET MVC IOC之Unity攻略 一.你知道IOC与DI吗? 1.IOC(Inversion of Control )——控制反转 即依赖对象不在被依赖模块的类中直接通过new来获取 先 ...

随机推荐

  1. 探索C#之6.0语法糖剖析

    阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和fin ...

  2. Android N开发 你需要知道的一切

    title: Android N开发 你需要知道的一切 tags: Android N,Android7.0,Android --- 转载请注明出处:http://www.cnblogs.com/yi ...

  3. 异步 HttpContext.Current 为空null 另一种解决方法

    1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...

  4. Tomcat常见问题及常用命令

    很长时间不用tomcat好多命令都忘记了,所以准备自己记录下来,以便参考.刚好也希望可以开始养成记博客的好习惯. 1.查看java的版本号 进入java的安装目录后,使用命令:java -versio ...

  5. TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析

    TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...

  6. JavaScript基础学习-函数及作用域

    函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...

  7. 如何开启MySQL 5.7.12 的二进制日志

    1. 打开/etc下的my.cnf文件 2. 编辑它,添加内容: log_bin=binary-log   #二进制日志的文件名 server_id=1  #必须指定server_id,这是MySQL ...

  8. C#(或者说.NET/Mono)能做的那些事

    不做语言之争,只陈述事实: 1.桌面软件与服务 不仅是在Windows上,有了开源的Mono,在Apple Mac和Linux(如:Ubuntu)上也有C#的施展天地.并且还可以通过mkbundle工 ...

  9. 微软将向Linux用户提供SQL Server程序

    微软公司(Microsoft Corp., MSFT)将向Linux操作系统的用户提供旗下一项最赚钱的产品,这是该公司几年前无法想像的举措.这家软件巨头周一表示,将向免费的Linux Server提供 ...

  10. 【腾讯Bugly干货分享】OCS——史上最疯狂的iOS动态化方案

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/zctwM2Wf8c6_sxT_0yZvXg 导语 在 ...