IOC容器在框架中的应用

前言

  在上一篇我大致的介绍了这个系列所涉及到的知识点,在本篇我打算把IOC这一块单独提取出来讲,因为IOC容器在解除框架层与层之间的耦合有着不可磨灭的作用。当然在本系列前面的三篇中我也提供了一种基于反射的解耦方式,但是始终不是很优雅,运用到项目中显得别扭。目前,我所掌握的IOC容器主要有两个:一个是 unity,另一个则是spring.net,经过慎重的思考我还是决定选择unity 2.0做为本系列的IOC容器,原因主要有两个:第一,他是一个轻量级的容器且师出名门(微软),第二,它提供了简单的拦截机制,在它的基础上实现AOP显得非常的简单,下面开始我们今天的议题......

什么是IOC容器

  IOC容器是对控制反转与依赖注入的一种实现,关于什么是控制反转,什么是依赖注入,网上一搜一大把,我这里就不在多说了,我们需要关注的就是IOC容器到底能够为我们做些什么事情,其实说白了,IOC容器就是通过相应的配置,用来为我们创建实例,使我们摆脱了new的魔咒,这在层与层之间的解耦中有着重要的意义,至于层次间为什么要解耦请参见我的第一篇, 本文着重介绍unity 2.0,您需要在项目中添加对Microsoft.Practices.Unity.dll与Microsoft.Practices.Unity.Configuration.dll的引用,下面我通过简单doom来讲述它的运用,程序如图

IOC项目引用了IService项目,但并未引用service项目,IService项目中定义的是服务接口,Service项目引用了IService项目并实现了里面的服务接口。我们现在要做的事情就是在IOC中采用IService接口标识服务,在调用时采用unity容器读取配置文件帮助我们把接口实例化,其具体的服务来自Service项目(我们的IOC项目没有引用Service项目所以是无法new的),为了很好的运用Unity容器,我做了一下封装,代码如下:

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Configuration;
using System.Reflection;
namespace IOC
{
public class ServiceLocator
{
/// <summary>
/// IOC容器
/// </summary>
private readonly IUnityContainer container;
private static readonly ServiceLocator instance = new ServiceLocator();
/// <summary>
/// 服务定位器单例
/// </summary>
public static ServiceLocator Instance
{
get { return instance; }
}
private ServiceLocator()
{
//读取容器配置文件
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
//创建容器
container = new UnityContainer();
//配置容器
section.Configure(container);
}
#region
/// <summary>
/// 创建构造函数参数
/// </summary>
/// <param name="overridedArguments"></param>
/// <returns></returns>
private IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments)
{
List<ParameterOverride> overrides = new List<ParameterOverride>();
Type argumentsType = overridedArguments.GetType();
argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToList()
.ForEach(property =>
{
var propertyValue = property.GetValue(overridedArguments, null);
var propertyName = property.Name;
overrides.Add(new ParameterOverride(propertyName, propertyValue));
});
return overrides;
}
#endregion #region 公共方法
/// <summary>
/// 创建指定类型的容器
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T GetService<T>()
{
return container.Resolve<T>();
}
/// <summary>
/// 根据指定名称的注册类型
/// 创建指定的类型
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">注册类型配置节点名称</param>
/// <returns></returns>
public T GetService<T>(string name)
{
return container.Resolve<T>(name);
}
/// <summary>
/// 用指定的构造函数参数
/// 创建实体
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="overridedArguments">属性名对应参数名,属性值对应
/// 参数值得动态参数实体</param>
/// <returns></returns>
public T GetService<T>(object overridedArguments)
{
var overrides = GetParameterOverrides(overridedArguments);
return container.Resolve<T>(overrides.ToArray());
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="overridedArguments"></param>
/// <returns></returns>
public T GetService<T>(string name,object overridedArguments)
{
var overrides = GetParameterOverrides(overridedArguments);
return container.Resolve<T>(name, overrides.ToArray());
}
#endregion
}
}

好了,下面开始我们的测试,我们首先在IService项目创建一个ISayHello服务接口代码如下

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IService
{
public interface ISayHello
{
string hello();
}
}

下面我们在Service项目中创建一个ChineseSayHello服务实现ISayHello接口代码如下

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IService;
namespace Service
{
public class ChineseSayHello : ISayHello
{
public string hello()
{
return "你好";
}
}
}

下面我们创建一个测试页面Test.aspx,后台代码如下

 
using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace IOC
{
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ISayHello sayhello = ServiceLocator.Instance.GetService<ISayHello>();
showInfo.InnerText = sayhello.hello();
}
}
}

好,下面来看一看我们的配置文件

<?xml version="1.0" encoding="utf-8"?>

<!--
有关如何配置 ASP.NET 应用程序的详细消息,请访问
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="IService.ISayHello,IService" mapTo="Service.ChineseSayHello,Service">
</register>
</container>
</unity>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
</configuration>

<register/>节点是告诉容器我要向容器中注册一个ISayHello接口类型,并且当每次要创建的ISayHello类型的时候都映射到ChineseSayHello实例。我们执行程序,得到的结果为:你好,这说明我们的容器正确的为我们创建ChineseSayHello实例。如果有一天我们觉得ChineseSayHello不好,我们想换一个服务来实现ISayHello,比如:EnglishSayHello,从而替代ChineseSayHello,我们仅需要创建一个EnglishSayHello类型,修改下配置文件,如下

 
 

程序的运行结果为:hello,实例创建成功。简简单单的一个例证,我们看见了IOC容器在给我们带来的巨大好处,我们IOC层根本不再依赖于具体的服务,我们想要什么实例配置下文件即可,这样极大的增加了程序的灵活性与可扩张性.

下面,我们来讨论一下容器实例的生命周期,也就是实例在容器中的存活时间。举个例子,我们在同样的配置文件下连续创建多个ISayHello服务实例,很显然,这样的多个实例是来自同样的类型的,现在我们关心的是容器是每一次都会为我们创建该类型的实例,还是仅仅只为我们创建一个,以后所有的ISayHello都引用同一个实例呢?我们测试下,代码如下

 

我们得到的结果是False,很显然容器每次都为我们去创建了一个实例。事实上Unity容器创建实例的机制是这样的:首先去容器中查找有没有这样的实例还保持在容器中,如果有的话则直接拿出来,如果没有的话则重新去创建一个。现在关键的问题是容器采用什么用的机制去保存这些被创建出来的实例,也就是实例在容器中的生命周期,在默认的情况下,实例被创建出来,容器即不再保存该实例,故在下次创建的时候容器找不到这样的实例,从而重新创建该类型实例,事实上实例的生命周期是可以配置的,我们甚至可以自定义实例的生命周期,下面我们修改下配置文件,设置实例的lifetime类型为singleton,即让实例永远保持在容器中,如下

 

我们在运行程序,得到的结果是:True,说明我们每次都引用了同一个实例,容器很好的帮我们实现了单例模式,除了singleton外,容器还默认了其他的几种实例生命周期,这里就不在多说了。注:我们所说的实例生命周期不是指实例的创建到销毁,而是指实例在容器中创建,受容器管辖的时间范围。

Unity容器支持为一个接口或者基类注册多个映射节点,但是每个节点需要采用不同的名称标识,在创建实例的时候,也通过该节点名称来创建指定的映射实例,例如

 
 

结果为:hello+你好,我们成功的通过了配置文件中的注册节点名称来创建我们的具体服务实例。我们知道创建实例是需要调用实例的构造函数的,很显然容器默认的为我们调用了构造函数,倘若构造函数带有参数,则容器则会创建相应的参数实例。现在问题来了,假如我的参数是一个接口或者抽象类型怎么办? 很显然要能创建这样的参数我们就必须知道参数的映射类型, 看如下例子,我们在IService项目中重新创建一个接口

 

我们写一个服务实现该接口

 

配置文件如下

 
 

我们得到结果:你好。在配置文件中我们添加了两个注册节点,从结果中我们看见,容器默认选择了未命名的节点,倘若我们注释该节点程序将报错,程序没办法自动识别带名称的节点,要想让程序识别带名称的节点我们需要配置构造函数参数,配置如下

 

结果正确的显示为:hello,在这里顺便提一下如果我们取消english注册节点lifetime的注释,我们会发现每次创建ComSayHello_V2的参数将来自同一个实例的引用,原因请参见,上文的实例生命周期。
    当然我们也可以直接在配置文件的构造函数中指定,参数类型而避免注册其他类型节点,配置文件代码如下,结果一样

 

其实,我们还能够在配置文件中给参数赋值,但是如果参数是一个复杂类型,比如类的时候,我们就需要一个转换器,把字符串类型的值转换为指定的赋值类型,因为在配置文件中我们赋值的类型只能是string。转换器在平时实践中用的少,所以我不打算多说。需要注意的是,如果我们的类中有多个构造函数的话,那么容器默认总会选择参数最多的那个构造函数。
   以上所介绍的归根到底也只是一种构造函数注入。其实Unity还提供能属性注入与方法注入,即在创建实例的时候动态为某个属性赋值或者调用某个方法,其实这个要做到也蛮简单的,我们只需要在相应的属性上面打上[Dependency]特性,在方法上打上[InjectionMethod]特性即可,但是这两种方式对类的侵入性太强,不推荐使用

总结

本文简单的演示了Unity IOC的一些使用方法,因为在我的框架中,Unity在层次解耦中充当了重要的作用,除此之外Unity其实还能实现的AOP拦截,但是由于篇幅的原因不再多讲,在这里要提醒大家务必理解实体的生命周期,因为这对实现单元工作模式有着重要的意义。在我的系列前三篇中,我都是采用了是反射来解耦,有兴趣的朋友可以尝试下用Unity取代它。我目前写的案例与前面系列的版本框架有很大的差异,所以有些知识点必须和大家说明,相信在接下来的一到两篇中,就能与大伙见面,祝大伙周末愉快。本篇测试源码请点击这里

 
 
分类: 框架

IOC容器在框架中的应用的更多相关文章

  1. 企业级应用框架(五)IOC容器在框架中的应用

    前言 在上一篇我大致的介绍了这个系列所涉及到的知识点,在本篇我打算把IOC这一块单独提取出来讲,因为IOC容器在解除框架层与层之间的耦合有着不可磨灭的作用.当然在本系列前面的三篇中我也提供了一种基于反 ...

  2. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  3. Spring框架学习[IoC容器高级特性]

    1.通过前面4篇文章对Spring IoC容器的源码分析,我们已经基本上了解了Spring IoC容器对Bean定义资源的定位.读入和解析过程,同时也清楚了当用户通过getBean方法向IoC容器获取 ...

  4. Spring核心原理之 IoC容器中那些鲜为人知的细节(3)

    本文节选自<Spring 5核心原理> Spring IoC容器还有一些高级特性,如使用lazy-init属性对Bean预初始化.使用FactoryBean产生或者修饰Bean对象的生成. ...

  5. 通过中看不中用的代码分析Ioc容器,依赖注入....

    /** * 通过生产拥有超能力的超人实例 来理解IOC容器 */ //超能力模组接口 interface SuperModuleInterface{ public function activate( ...

  6. 《精通Spring 4.X企业应用开发实战》读书笔记1-1(IoC容器和Bean)

    很长一段时间关注在Java Web开发的方向上,提及到Jave Web开发就绕不开Spring全家桶系列,使用面向百度,谷歌的编程方法能够完成大部分的工作.但是这种不系统的了解总觉得自己的知识有所欠缺 ...

  7. Spring学习(一):理解IoC容器

    序言 记得刚毕业那会儿,出来招工作被问到Spring的核心时,都觉得简单的一笔,直接说不就是IoC(控制反转)和DI(依赖注入)么,然后省略一万字对两个名词的解释.最近空来整理了一下Spring中Io ...

  8. Spring IOC容器的实现原理

    1 概述 1.1 依赖反转模式 在Java中,一个复杂的功能一般都需要由两个或者两个以上的类通过彼此合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用.如果这个获取依赖对象的过程需要自己去 ...

  9. Spring IOC和IOC容器

    IOC的核心理念即是控制反转.将对依赖的控制从具体业务对象手中转交到平台或框架中,需要的时候再由平台或框架注入到具体业务对象中.可以说依赖注入是控制反转的实现方式. IOC的优点: 降低代码耦合度 减 ...

随机推荐

  1. ZOJ 3822 可能性DP

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3822 本场比赛之前,我记得.见WALK概率路DP称号.那么它应该是可以考虑 ...

  2. Android Context MODE的四种模式解析(转)

    Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中.可以使用Context ...

  3. CentOS 7 下安装 LEMP 服务(nginx、MariaDB/MySQL 和 php)

    原文 CentOS 7 下安装 LEMP 服务(nginx.MariaDB/MySQL 和 php) LEMP 组合包是一款日益流行的网站服务组合软件包,在许多生产环境中的核心网站服务上起着强有力的作 ...

  4. Linux对于录音

    一.原理简介 在Linux下,记录--从dsp读取数据.播放--至dsp设备写入数据. 开发板採用声卡UDA1341实现音频编解码,完毕A/D和D/A转换,芯片UDA1341与CPU的连接图例如以下: ...

  5. RabbitMQ (两)工作队列

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/37620057 本系列教程主要来自于官网新手教程的翻译,然后自己进行了部分的改动与 ...

  6. SQL Server 备份和还原全攻略

    原文:SQL Server 备份和还原全攻略 一.知识点 完全备份: 备份全部选中的文件夹,并不依赖文件的存档属性来确定备份那些文件.(在备份过程中,任何现有的标记都被清除,每个文件都被标记为已备份, ...

  7. JavaEE(4) - JMS实现企业PTP消息处理

    1. 在Weblogic服务器上配置PTP消息目的 配置持久化: Services-->Persistence Stores-->New(Create FileStore, Create ...

  8. 怪异php 语法, 求解!

    查找php馍用来推断是否串串返回值和方法 strpos很奇怪. 请看下面的语句: echo "A1: ".(strpos("csd","c" ...

  9. XSS Overview

    什么是XSS? 跨站脚本攻击(Cross Site Scripting):攻击者往Web页面里插入恶意脚本,当用户浏览该页面时,嵌入页面的脚本代码会被执行,从而达到恶意攻击用户的特殊目的.恶意的内容通 ...

  10. UITableView的常用方法

    一.UITableView的代理方法 #pragma mark 每一行的高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtI ...