在哪个网站做外快设计,查看网站空间大小,wordpress 晒单,微信开发者工具可视化怎么打开前言 在上一篇我大致的介绍了这个系列所涉及到的知识点#xff0c;在本篇我打算把IOC这一块单独提取出来讲#xff0c;因为IOC容器在解除框架层与层之间的耦合有着不可磨灭的作用。当然在本系列前面的三篇中我也提供了一种基于反射的解耦方式#xff0c;但是始终不是很优雅在本篇我打算把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容器/// /summaryprivate readonly IUnityContainer container;private static readonly ServiceLocator instance new ServiceLocator();/// summary/// 服务定位器单例/// /summarypublic static ServiceLocator Instance{get { return instance; }}private ServiceLocator(){//读取容器配置文件UnityConfigurationSection section (UnityConfigurationSection)ConfigurationManager.GetSection(unity);//创建容器container new UnityContainer();//配置容器section.Configure(container);}#region /// summary/// 创建构造函数参数/// /summary/// param nameoverridedArguments/param/// returns/returnsprivate IEnumerableParameterOverride GetParameterOverrides(object overridedArguments){ListParameterOverride overrides new ListParameterOverride();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 nameT/typeparam/// returns/returnspublic T GetServiceT(){return container.ResolveT();}/// summary/// 根据指定名称的注册类型/// 创建指定的类型/// /summary/// typeparam nameT/typeparam/// param namename注册类型配置节点名称/param/// returns/returnspublic T GetServiceT(string name){return container.ResolveT(name);}/// summary/// 用指定的构造函数参数/// 创建实体/// /summary/// typeparam nameT实体类型/typeparam/// param nameoverridedArguments属性名对应参数名属性值对应/// 参数值得动态参数实体/param/// returns/returnspublic T GetServiceT(object overridedArguments){var overrides GetParameterOverrides(overridedArguments);return container.ResolveT(overrides.ToArray());}/// summary/// /// /summary/// typeparam nameT/typeparam/// param namename/param/// param nameoverridedArguments/param/// returns/returnspublic T GetServiceT(string name,object overridedArguments){var overrides GetParameterOverrides(overridedArguments);return container.ResolveT(name, overrides.ToArray());}#endregion}
} View Code 好了下面开始我们的测试我们首先在IService项目创建一个ISayHello服务接口代码如下 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace IService
{public interface ISayHello{string hello();}
} View Code 下面我们在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 你好;}}
} View Code 下面我们创建一个测试页面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.GetServiceISayHello();showInfo.InnerText sayhello.hello();}}
} View Code 好下面来看一看我们的配置文件 ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainerregister typeIService.ISayHello,IService mapToService.ChineseSayHello,Service/register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration register/节点是告诉容器我要向容器中注册一个ISayHello接口类型并且当每次要创建的ISayHello类型的时候都映射到ChineseSayHello实例。我们执行程序得到的结果为你好这说明我们的容器正确的为我们创建ChineseSayHello实例。如果有一天我们觉得ChineseSayHello不好我们想换一个服务来实现ISayHello比如EnglishSayHello从而替代ChineseSayHello我们仅需要创建一个EnglishSayHello类型修改下配置文件如下 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IService;namespace Service
{public class EnglishSayHello : ISayHello{public string hello(){return hello;}}
} View Code ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainer!--register typeIService.ISayHello,IService mapToService.ChineseSayHello,Service/register--register typeIService.ISayHello,IService mapToService.EnglishSayHello,Service/register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration View Code 程序的运行结果为hello实例创建成功。简简单单的一个例证我们看见了IOC容器在给我们带来的巨大好处我们IOC层根本不再依赖于具体的服务我们想要什么实例配置下文件即可这样极大的增加了程序的灵活性与可扩张性. 下面我们来讨论一下容器实例的生命周期也就是实例在容器中的存活时间。举个例子我们在同样的配置文件下连续创建多个ISayHello服务实例很显然这样的多个实例是来自同样的类型的现在我们关心的是容器是每一次都会为我们创建该类型的实例还是仅仅只为我们创建一个以后所有的ISayHello都引用同一个实例呢我们测试下代码如下 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.GetServiceISayHello();ISayHello sayhello1 ServiceLocator.Instance.GetServiceISayHello();showInfo.InnerText sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();}}
} View Code 我们得到的结果是False,很显然容器每次都为我们去创建了一个实例。事实上Unity容器创建实例的机制是这样的首先去容器中查找有没有这样的实例还保持在容器中如果有的话则直接拿出来如果没有的话则重新去创建一个。现在关键的问题是容器采用什么用的机制去保存这些被创建出来的实例,也就是实例在容器中的生命周期在默认的情况下实例被创建出来容器即不再保存该实例故在下次创建的时候容器找不到这样的实例从而重新创建该类型实例事实上实例的生命周期是可以配置的我们甚至可以自定义实例的生命周期下面我们修改下配置文件设置实例的lifetime类型为singleton即让实例永远保持在容器中如下 ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainer!--register typeIService.ISayHello,IService mapToService.ChineseSayHello,Service/register--register typeIService.ISayHello,IService mapToService.EnglishSayHello,Servicelifetime typesingleton//register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration View Code 我们在运行程序得到的结果是True,说明我们每次都引用了同一个实例容器很好的帮我们实现了单例模式除了singleton外容器还默认了其他的几种实例生命周期这里就不在多说了。注我们所说的实例生命周期不是指实例的创建到销毁而是指实例在容器中创建受容器管辖的时间范围。 Unity容器支持为一个接口或者基类注册多个映射节点但是每个节点需要采用不同的名称标识在创建实例的时候也通过该节点名称来创建指定的映射实例例如 ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainerregister typeIService.ISayHello,IService mapToService.ChineseSayHello,Service/registerregister nameenglish typeIService.ISayHello,IService mapToService.EnglishSayHello,Service/register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration View Code 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.GetServiceISayHello();ISayHello sayhello1 ServiceLocator.Instance.GetServiceISayHello(english);showInfo.InnerText string.Format({0}{1}, sayhello1.hello(), sayhello.hello());//showInfo.InnerText sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();}}
} View Code 结果为hello你好,我们成功的通过了配置文件中的注册节点名称来创建我们的具体服务实例。我们知道创建实例是需要调用实例的构造函数的很显然容器默认的为我们调用了构造函数倘若构造函数带有参数则容器则会创建相应的参数实例。现在问题来了假如我的参数是一个接口或者抽象类型怎么办? 很显然要能创建这样的参数我们就必须知道参数的映射类型, 看如下例子我们在IService项目中重新创建一个接口 using System;
using System.Collections.Generic;
using System.Linq; using System.Text; namespace IService { public interface ISay { string Say(); } } View Code 我们写一个服务实现该接口 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IService;namespace Service
{public class ComSayHello_V2 : ISay{public ISayHello Chinese { get; set; }public ComSayHello_V2(ISayHello chinese){this.Chinese chinese;}public string Say(){return this.Chinese.hello();}}
} View Code 配置文件如下 ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainerregister typeIService.ISayHello,IService mapToService.ChineseSayHello,Service/registerregister nameenglish typeIService.ISayHello,IService mapToService.EnglishSayHello,Service/registerregister typeIService.ISay,IService mapToService.ComSayHello_V2,Service/register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration View Code 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.GetServiceISayHello();//ISayHello sayhello1 ServiceLocator.Instance.GetServiceISayHello(english);//showInfo.InnerText string.Format({0}{1}, sayhello1.hello(), sayhello.hello());//showInfo.InnerText sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();ISay say ServiceLocator.Instance.GetServiceISay();showInfo.InnerTextsay.Say();}}
} View Code 我们得到结果你好。在配置文件中我们添加了两个注册节点从结果中我们看见容器默认选择了未命名的节点倘若我们注释该节点程序将报错程序没办法自动识别带名称的节点要想让程序识别带名称的节点我们需要配置构造函数参数配置如下 ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainer!--register typeIService.ISayHello,IService mapToService.ChineseSayHello,Service/register--register nameenglish typeIService.ISayHello,IService mapToService.EnglishSayHello,Service!--lifetime typesingleton/--/registerregister typeIService.ISay,IService mapToService.ComSayHello_V2,Serviceconstructorparam namesay dependencyNameenglish/param/constructor/register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration View Code 结果正确的显示为:hello在这里顺便提一下如果我们取消english注册节点lifetime的注释我们会发现每次创建ComSayHello_V2的参数将来自同一个实例的引用原因请参见上文的实例生命周期。 当然我们也可以直接在配置文件的构造函数中指定参数类型而避免注册其他类型节点配置文件代码如下结果一样 ?xml version1.0 encodingutf-8?!--有关如何配置 ASP.NET 应用程序的详细消息请访问http://go.microsoft.com/fwlink/?LinkId169433--
configurationconfigSectionssection nameunity typeMicrosoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration//configSectionsunity xmlnshttp://schemas.microsoft.com/practices/2010/unitycontainerregister typeIService.ISay,IService mapToService.ComSayHello_V2,Serviceconstructorparam namesay dependencyTypeService.EnglishSayHello,Service/param/constructor/register/container/unitysystem.webcompilation debugtrue targetFramework4.0 //system.web
/configuration View Code 其实我们还能够在配置文件中给参数赋值但是如果参数是一个复杂类型比如类的时候我们就需要一个转换器把字符串类型的值转换为指定的赋值类型因为在配置文件中我们赋值的类型只能是string。转换器在平时实践中用的少所以我不打算多说。需要注意的是如果我们的类中有多个构造函数的话那么容器默认总会选择参数最多的那个构造函数。 以上所介绍的归根到底也只是一种构造函数注入。其实Unity还提供能属性注入与方法注入即在创建实例的时候动态为某个属性赋值或者调用某个方法其实这个要做到也蛮简单的我们只需要在相应的属性上面打上[Dependency]特性在方法上打上[InjectionMethod]特性即可但是这两种方式对类的侵入性太强不推荐使用 总结 本文简单的演示了Unity IOC的一些使用方法因为在我的框架中Unity在层次解耦中充当了重要的作用除此之外Unity其实还能实现的AOP拦截但是由于篇幅的原因不再多讲在这里要提醒大家务必理解实体的生命周期因为这对实现单元工作模式有着重要的意义。在我的系列前三篇中我都是采用了是反射来解耦有兴趣的朋友可以尝试下用Unity取代它。我目前写的案例与前面系列的版本框架有很大的差异所以有些知识点必须和大家说明相信在接下来的一到两篇中就能与大伙见面,祝大伙周末愉快。本篇测试源码请点击这里 转载于:https://www.cnblogs.com/shaoshun/p/3868968.html