[IoC容器Unity]第四回:使用范例解耦依赖注入机制

出处:http://www.cnblogs.com/qqlin/archive/2012/10/18/2720830.html


1.引言 

前面几个章节介绍了Unity的基本使用,主要分为程序和配置文件两种方法的使用,可以参考一下链接

本节作为结束篇,将介绍一下在项目中如何应用Unity。   

2.范例

Unity应用广泛,在很多开源项目中都可以找到Unity的身影。就拿微软的开源项目新闻发布系统Kigg举例,Kigg的依赖注入就是使用到了Unity,大家可以下载。Kigg是MVC应用的一个推荐范例,本节介绍一下其中的依赖注入IoC容器,该容器在Kigg.Core项目,Infrastructure目录下的IOC目录,该目录下有4个类,如下图

先看看IDependencyResolver接口声明

复制代码

publicinterfaceIDependencyResolver:Idisposable
{///<summary>
///注册T类型实例///</summary>
///<typeparamname="T"></typeparam>
///<paramname="instance"></param>
voidRegister<T>(Tinstance);///<summary>
///注入
///</summary>
///<typeparamname="T"></typeparam>
///<paramname="existing"></param>
voidInject<T>(Texisting);///<summary>
///解析///</summary>
///<typeparamname="T"></typeparam>
///<paramname="type"></param>
///<returns></returns>
TResolve<T>(Typetype);

TResolve<T>(Typetype,stringname);

TResolve<T>();

TResolve<T>(stringname);

IEnumerable<T>ResolveAll<T>();
}

复制代码

看到该接口定义,我们很快会想到Unity中的IUnityContainer容器接口,对的,里面的方法和作用 跟IUnityContainer接口类似。
那为什么不直接使用IUnityContainer而要定义一个类似的接口IDependencyResolver呢?
可以看到Kigg的IDependencyResolver是定义在核心层Kigg.Core相当于基础架构层中,而这个层是一个核心库,其它层都会引用它,Kigg应用了一种像 适配器模式来进行封装。
就是系统中要应用其它的外部接口,比如Unity 和Sping.net的依赖注入的方法不统一,我们要进行封装,可以先定义一个公共接口,再利用Unity和其它组件来实现它,这就是IDependencyResolver的由来。

Kigg中已经利用Unity来实现IDependencyResolver接口,当然我们也可以用其他的依赖注入容器来实现它,下面看看UnityDependencyResolver的实现

复制代码

publicclassUnityDependencyResolver:disposableResource,IDependencyResolver
{//注入容器
privatereadonlyIUnityContainer_container;

[DebuggerStepThrough]publicUnityDependencyResolver():this(newUnityContainer())
{
UnityConfigurationSectionconfiguration=(UnityConfigurationSection)ConfigurationManager.GetSection("unity");
configuration.Containers.Default.Configure(_container);
}

[DebuggerStepThrough]publicUnityDependencyResolver(IUnityContainercontainer)
{
Check.Argument.IsNotNull(container,"container");

_container=container;
}

[DebuggerStepThrough]publicvoidRegister<T>(Tinstance)
{
Check.Argument.IsNotNull(instance,"instance");//注册实例_container.RegisterInstance(instance);
}

[DebuggerStepThrough]publicvoidInject<T>(Texisting)
{
Check.Argument.IsNotNull(existing,"existing");//注入加载_container.BuildUp(existing);
}

[DebuggerStepThrough]publicTResolve<T>(Typetype)
{
Check.Argument.IsNotNull(type,"type");//解析
return(T)_container.Resolve(type);
}

[DebuggerStepThrough]publicTResolve<T>(Typetype,stringname)
{
Check.Argument.IsNotNull(type,"type");
Check.Argument.IsNotEmpty(name,"name");return(T)_container.Resolve(type,name);
}

[DebuggerStepThrough]publicTResolve<T>()
{return_container.Resolve<T>();
}

[DebuggerStepThrough]publicTResolve<T>(stringname)
{
Check.Argument.IsNotEmpty(name,"name");return_container.Resolve<T>(name);
}

[DebuggerStepThrough]publicIEnumerable<T>ResolveAll<T>()
{//解析容器中所有
IEnumerable<T>namedInstances=_container.ResolveAll<T>();
TunnamedInstance=default(T);try
{
unnamedInstance=_container.Resolve<T>();
}catch(ResolutionFailedException)
{//Whendefaultinstanceismissing}if(Equals(unnamedInstance,default(T)))
{returnnamedInstances;
}returnnewReadOnlyCollection<T>(newList<T>(namedInstances){unnamedInstance});
}

[DebuggerStepThrough]protectedoverridevoiddispose(booldisposing)
{if(disposing)
{
_container.dispose();
}base.dispose(disposing);
}
}

可以看到UnityDependencyResolver的认构造函数是加载配置文件配置文件在Web.Config中)来初始化IUnityContainer,你也可以用编程的方式。
实现方式中没有继承IUnityContainer或者UnityContainer,而是和IUnityContainer是组合关系,这样更加的灵活,这是对象的Adapter模式,就是组合模式。如果有其它的IoC容器,如Windsor/StructureMap/Spring.Net等等,可以实现IDependencyResolver接口即可。

使用时,只需要实例化对应的IDependencyResolver对象就可以了,Kigg中为了更好的控制IDependencyResolver对象的创建,利用了工厂方法来创建。
先看看工厂接口IDependencyResolverFactory

复制代码

publicinterfaceIDependencyResolverFactory
{///<summary>
///创建IDependencyResolver的实例///</summary>
///<returns></returns>IDependencyResolverCreateInstance();
}

看到定义,只有一个方法CreateInstance,就是用来创建IDependencyResolver对象,我们可以实现该工厂,可以直接new UnityDependencyResolver来创建,Kigg中利用配置文件方式,看DependencyResolverFactory的声明如下:

复制代码

publicclassDependencyResolverFactory:IDependencyResolverFactory
{privatereadonlyType_resolverType;publicDependencyResolverFactory(stringresolverTypeName)
{
Check.Argument.IsNotEmpty(resolverTypeName,"resolverTypeName");//GetType(名字,是否区分大小,是否异常)
_resolverType=Type.GetType(resolverTypeName,true,true);
}publicDependencyResolverFactory():this(newConfigurationManagerWrapper().AppSettings["dependencyResolverTypeName"])
{
}publicIDependencyResolverCreateInstance()
{//根据类型创建实例对象
returnActivator.CreateInstance(_resolverType)asIDependencyResolver;
}
}

可以看到认构造函数是读取配置文件dependencyResolverTypeName节点,利用反射Activator.CreateInstance进行创建,看看dependencyResolverTypeName节点定义,在Kigg.Web项目的配置文件中,如下:

<appSettings>
<clear/>
<addkey="dependencyResolverTypeName"value="Kigg.Infrastructure.EnterpriseLibrary.UnityDependencyResolver,Kigg.Infrastructure.EnterpriseLibrary"/>
</appSettings>

还有其它IoC容器实现时,只要更改配置文件就行。

使用时可以调用工厂方法进行创建IDependencyResolver对象,每次使用时都得利用工厂来创建,IDependencyResolver里面的方法肯定都是实例方法,使用也不方便,Kigg为我们进行封装成静态方法,看IoC类的声明

复制代码

publicstaticclassIoC
{//解析器
privatestaticIDependencyResolver_resolver;///<summary>
///初始化,创建实例对象///</summary>
///<paramname="factory"></param>[DebuggerStepThrough]publicstaticvoidInitializeWith(IDependencyResolverFactoryfactory)
{
Check.Argument.IsNotNull(factory,"factory");

_resolver=factory.CreateInstance();
}///<summary>
///注册对象///</summary>
///<typeparamname="T"></typeparam>
///<paramname="instance"></param>[DebuggerStepThrough]publicstaticvoidRegister<T>(Tinstance)
{
Check.Argument.IsNotNull(instance,"instance");

_resolver.Register(instance);
}///<summary>
///注入对象///</summary>
///<typeparamname="T"></typeparam>
///<paramname="existing"></param>[DebuggerStepThrough]publicstaticvoidInject<T>(Texisting)
{
Check.Argument.IsNotNull(existing,"existing");

_resolver.Inject(existing);
}///<summary>
///解析对象///</summary>
///<typeparamname="T"></typeparam>
///<paramname="type"></param>
///<returns></returns>[DebuggerStepThrough]publicstaticTResolve<T>(Typetype)
{
Check.Argument.IsNotNull(type,"type");return_resolver.Resolve<T>(type);
}///<summary>
///解析对象///</summary>
///<typeparamname="T"></typeparam>
///<paramname="type"></param>
///<paramname="name"></param>
///<returns></returns>[DebuggerStepThrough]publicstaticTResolve<T>(Typetype,"name");return_resolver.Resolve<T>(type,name);
}///<summary>
///解析对象///</summary>
///<typeparamname="T"></typeparam>
///<returns></returns>[DebuggerStepThrough]publicstaticTResolve<T>()
{return_resolver.Resolve<T>();
}///<summary>
///解析对象///</summary>
///<typeparamname="T"></typeparam>
///<paramname="name"></param>
///<returns></returns>[DebuggerStepThrough]publicstaticTResolve<T>(stringname)
{
Check.Argument.IsNotEmpty(name,"name");return_resolver.Resolve<T>(name);
}///<summary>
///解析对象///</summary>
///<typeparamname="T"></typeparam>
///<returns></returns>[DebuggerStepThrough]publicstaticIEnumerable<T>ResolveAll<T>()
{return_resolver.ResolveAll<T>();
}///<summary>
///销毁///</summary>[DebuggerStepThrough]publicstaticvoidReset()
{if(_resolver!=null)
{
_resolver.dispose();
}
}
}

IDependencyResolver是IoC的一个私有静态成员,私有的,那怎么创建对象,IoC类有一个InitializeWith(IDependencyResolverFactory factory),利用工厂方法来创建,以后我们只要使用IoC这个类就行。IoC静态类并没有在静态构造函数中初始化IDependencyResolver,考虑到依赖,只依赖比较稳定的接口,而不会依赖具体的实现如DependencyResolverFactory,这样就可以提供方法供外面访问来进行创建IDependencyResolver对象。

那IDependencyResolver什么时候会创建,由于没有在构造函数中实现创建,必定要调用IoC的InitializeWith方法,我们可以找到引用,看到一个启动引导Bootstrapper类如下:

复制代码

publicstaticclassBootstrapper
{staticBootstrapper()
{try
{
IoC.InitializeWith(newDependencyResolverFactory());
}catch(ArgumentException)
{//ConfigfileisMissing}
}publicstaticvoidRun()
{
IoC.ResolveAll<IBootstrapperTask>().ForEach(t=>t.Execute());
}
}

在Bootstrapper的构造函数中进行了IDependencyResolver的创建,即在第一次使用Bootstrapper时会创建,那肯定的是Bootstrapper一定要在IoC之前使用啊,不然在使用IoC类时肯定报错,不用担心,Bootstrapper使用的很早,因为它是一个引导启动类,查找引用,可以看到在Kigg.Web下的Global.asax文件中找到,声明如下

复制代码

publicclassGlobalApplication:HttpApplication
{publicstaticvoidOnStart()
{
Bootstrapper.Run();
Log.Info("ApplicationStarted");
}publicstaticvoidOnEnd()
{
Log.Warning("ApplicationEnded");
IoC.Reset();
}protectedvoidApplication_Start()
{
OnStart();
}protectedvoidApplication_End()
{
OnEnd();
}
}

原来在Application_Start中,程序启动时就使用到了,好了,以后就直接使用Ioc来创建依赖对象吧,不用new了,Unity的配置文件都是在Web.Config中,就不介绍了,Kigg的依赖容器就介绍完毕了。

3.小结

我们使用时如果想要IoC容器容易扩展容易使用可以参照Kigg。

IUnityContainer容器可以声明N个对象,那样就不好管理了,我们应该只要一个,即创建一次,可以使用static静态成员。

不用担心一个IUnityContainer容器中注册了太多对象关系而影响解析性能,IUnityContainer中是维护着许多字典,就是说注册100个跟注册100W个映射是一样的。

IUnityContainer可以通过编程映射和配置文件映射,推荐配置文件映射。

相关文章

迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图...
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,...
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定...
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个...
命令模式(Command)命令模式(Command)[Action/Transactio...
生成器模式(Builder)生成器模式(Builder)意图:将一个对...