企业级应用框架(五)IOC容器在框架中的应用
前言
在上一篇我大致的介紹了這個(gè)系列所涉及到的知識(shí)點(diǎn),在本篇我打算把IOC這一塊單獨(dú)提取出來(lái)講,因?yàn)镮OC容器在解除框架層與層之間的耦合有著不可磨滅的作用。當(dāng)然在本系列前面的三篇中我也提供了一種基于反射的解耦方式,但是始終不是很優(yōu)雅,運(yùn)用到項(xiàng)目中顯得別扭。目前,我所掌握的IOC容器主要有兩個(gè):一個(gè)是 unity,另一個(gè)則是spring.net,經(jīng)過(guò)慎重的思考我還是決定選擇unity 2.0做為本系列的IOC容器,原因主要有兩個(gè):第一,他是一個(gè)輕量級(jí)的容器且?guī)煶雒T(mén)(微軟),第二,它提供了簡(jiǎn)單的攔截機(jī)制,在它的基礎(chǔ)上實(shí)現(xiàn)AOP顯得非常的簡(jiǎn)單,下面開(kāi)始我們今天的議題......
什么是IOC容器
IOC容器是對(duì)控制反轉(zhuǎn)與依賴(lài)注入的一種實(shí)現(xiàn),關(guān)于什么是控制反轉(zhuǎn),什么是依賴(lài)注入,網(wǎng)上一搜一大把,我這里就不在多說(shuō)了,我們需要關(guān)注的就是IOC容器到底能夠?yàn)槲覀冏鲂┦裁词虑?#xff0c;其實(shí)說(shuō)白了,IOC容器就是通過(guò)相應(yīng)的配置,用來(lái)為我們創(chuàng)建實(shí)例,使我們擺脫了new的魔咒,這在層與層之間的解耦中有著重要的意義,至于層次間為什么要解耦請(qǐng)參見(jiàn)我的第一篇, 本文著重介紹unity 2.0,您需要在項(xiàng)目中添加對(duì)Microsoft.Practices.Unity.dll與Microsoft.Practices.Unity.Configuration.dll的引用,下面我通過(guò)簡(jiǎn)單doom來(lái)講述它的運(yùn)用,程序如圖
IOC項(xiàng)目引用了IService項(xiàng)目,但并未引用service項(xiàng)目,IService項(xiàng)目中定義的是服務(wù)接口,Service項(xiàng)目引用了IService項(xiàng)目并實(shí)現(xiàn)了里面的服務(wù)接口。我們現(xiàn)在要做的事情就是在IOC中采用IService接口標(biāo)識(shí)服務(wù),在調(diào)用時(shí)采用unity容器讀取配置文件幫助我們把接口實(shí)例化,其具體的服務(wù)來(lái)自Service項(xiàng)目(我們的IOC項(xiàng)目沒(méi)有引用Service項(xiàng)目所以是無(wú)法new的),為了很好的運(yùn)用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>/// 服務(wù)定位器單例/// </summary>public static ServiceLocator Instance{get { return instance; }}private ServiceLocator(){//讀取容器配置文件UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");//創(chuàng)建容器container = new UnityContainer();//配置容器 section.Configure(container);}#region /// <summary>/// 創(chuàng)建構(gòu)造函數(shù)參數(shù)/// </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>/// 創(chuàng)建指定類(lèi)型的容器/// </summary>/// <typeparam name="T"></typeparam>/// <returns></returns>public T GetService<T>(){return container.Resolve<T>();}/// <summary>/// 根據(jù)指定名稱(chēng)的注冊(cè)類(lèi)型/// 創(chuàng)建指定的類(lèi)型/// </summary>/// <typeparam name="T"></typeparam>/// <param name="name">注冊(cè)類(lèi)型配置節(jié)點(diǎn)名稱(chēng)</param>/// <returns></returns>public T GetService<T>(string name){return container.Resolve<T>(name);}/// <summary>/// 用指定的構(gòu)造函數(shù)參數(shù)/// 創(chuàng)建實(shí)體/// </summary>/// <typeparam name="T">實(shí)體類(lèi)型</typeparam>/// <param name="overridedArguments">屬性名對(duì)應(yīng)參數(shù)名,屬性值對(duì)應(yīng)/// 參數(shù)值得動(dòng)態(tài)參數(shù)實(shí)體</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} } View Code好了,下面開(kāi)始我們的測(cè)試,我們首先在IService項(xiàng)目創(chuàng)建一個(gè)ISayHello服務(wù)接口代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace IService {public interface ISayHello{string hello();} } View Code下面我們?cè)赟ervice項(xiàng)目中創(chuàng)建一個(gè)ChineseSayHello服務(wù)實(shí)現(xiàn)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下面我們創(chuàng)建一個(gè)測(cè)試頁(yè)面Test.aspx,后臺(tái)代碼如下
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();}} } View Code好,下面來(lái)看一看我們的配置文件
<?xml version="1.0" encoding="utf-8"?><!--有關(guān)如何配置 ASP.NET 應(yīng)用程序的詳細(xì)消息,請(qǐng)?jiān)L問(wèn)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/>節(jié)點(diǎn)是告訴容器我要向容器中注冊(cè)一個(gè)ISayHello接口類(lèi)型,并且當(dāng)每次要?jiǎng)?chuàng)建的ISayHello類(lèi)型的時(shí)候都映射到ChineseSayHello實(shí)例。我們執(zhí)行程序,得到的結(jié)果為:你好,這說(shuō)明我們的容器正確的為我們創(chuàng)建ChineseSayHello實(shí)例。如果有一天我們覺(jué)得ChineseSayHello不好,我們想換一個(gè)服務(wù)來(lái)實(shí)現(xiàn)ISayHello,比如:EnglishSayHello,從而替代ChineseSayHello,我們僅需要?jiǎng)?chuàng)建一個(gè)EnglishSayHello類(lèi)型,修改下配置文件,如下
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 version="1.0" encoding="utf-8"?><!--有關(guān)如何配置 ASP.NET 應(yīng)用程序的詳細(xì)消息,請(qǐng)?jiān)L問(wèn)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>--><register type="IService.ISayHello,IService" mapTo="Service.EnglishSayHello,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web> </configuration> View Code程序的運(yùn)行結(jié)果為:hello,實(shí)例創(chuàng)建成功。簡(jiǎn)簡(jiǎn)單單的一個(gè)例證,我們看見(jiàn)了IOC容器在給我們帶來(lái)的巨大好處,我們IOC層根本不再依賴(lài)于具體的服務(wù),我們想要什么實(shí)例配置下文件即可,這樣極大的增加了程序的靈活性與可擴(kuò)張性.
????下面,我們來(lái)討論一下容器實(shí)例的生命周期,也就是實(shí)例在容器中的存活時(shí)間。舉個(gè)例子,我們?cè)谕瑯拥呐渲梦募逻B續(xù)創(chuàng)建多個(gè)ISayHello服務(wù)實(shí)例,很顯然,這樣的多個(gè)實(shí)例是來(lái)自同樣的類(lèi)型的,現(xiàn)在我們關(guān)心的是容器是每一次都會(huì)為我們創(chuàng)建該類(lèi)型的實(shí)例,還是僅僅只為我們創(chuàng)建一個(gè),以后所有的ISayHello都引用同一個(gè)實(shí)例呢?我們測(cè)試下,代碼如下
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>();ISayHello sayhello1 = ServiceLocator.Instance.GetService<ISayHello>();showInfo.InnerText = sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();}} } View Code我們得到的結(jié)果是False,很顯然容器每次都為我們?nèi)?chuàng)建了一個(gè)實(shí)例。事實(shí)上Unity容器創(chuàng)建實(shí)例的機(jī)制是這樣的:首先去容器中查找有沒(méi)有這樣的實(shí)例還保持在容器中,如果有的話(huà)則直接拿出來(lái),如果沒(méi)有的話(huà)則重新去創(chuàng)建一個(gè)?,F(xiàn)在關(guān)鍵的問(wèn)題是容器采用什么用的機(jī)制去保存這些被創(chuàng)建出來(lái)的實(shí)例,也就是實(shí)例在容器中的生命周期,在默認(rèn)的情況下,實(shí)例被創(chuàng)建出來(lái),容器即不再保存該實(shí)例,故在下次創(chuàng)建的時(shí)候容器找不到這樣的實(shí)例,從而重新創(chuàng)建該類(lèi)型實(shí)例,事實(shí)上實(shí)例的生命周期是可以配置的,我們甚至可以自定義實(shí)例的生命周期,下面我們修改下配置文件,設(shè)置實(shí)例的lifetime類(lèi)型為singleton,即讓實(shí)例永遠(yuǎn)保持在容器中,如下
<?xml version="1.0" encoding="utf-8"?><!--有關(guān)如何配置 ASP.NET 應(yīng)用程序的詳細(xì)消息,請(qǐng)?jiān)L問(wèn)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>--><register type="IService.ISayHello,IService" mapTo="Service.EnglishSayHello,Service"><lifetime type="singleton"/></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web> </configuration> View Code我們?cè)谶\(yùn)行程序,得到的結(jié)果是:True,說(shuō)明我們每次都引用了同一個(gè)實(shí)例,容器很好的幫我們實(shí)現(xiàn)了單例模式,除了singleton外,容器還默認(rèn)了其他的幾種實(shí)例生命周期,這里就不在多說(shuō)了。注:我們所說(shuō)的實(shí)例生命周期不是指實(shí)例的創(chuàng)建到銷(xiāo)毀,而是指實(shí)例在容器中創(chuàng)建,受容器管轄的時(shí)間范圍。
??? Unity容器支持為一個(gè)接口或者基類(lèi)注冊(cè)多個(gè)映射節(jié)點(diǎn),但是每個(gè)節(jié)點(diǎn)需要采用不同的名稱(chēng)標(biāo)識(shí),在創(chuàng)建實(shí)例的時(shí)候,也通過(guò)該節(jié)點(diǎn)名稱(chēng)來(lái)創(chuàng)建指定的映射實(shí)例,例如
<?xml version="1.0" encoding="utf-8"?><!--有關(guān)如何配置 ASP.NET 應(yīng)用程序的詳細(xì)消息,請(qǐng)?jiān)L問(wèn)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><register name="english" type="IService.ISayHello,IService" mapTo="Service.EnglishSayHello,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.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.GetService<ISayHello>();ISayHello sayhello1 = ServiceLocator.Instance.GetService<ISayHello>("english");showInfo.InnerText = string.Format("{0}+{1}", sayhello1.hello(), sayhello.hello());//showInfo.InnerText = sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString(); }} } View Code結(jié)果為:hello+你好,我們成功的通過(guò)了配置文件中的注冊(cè)節(jié)點(diǎn)名稱(chēng)來(lái)創(chuàng)建我們的具體服務(wù)實(shí)例。我們知道創(chuàng)建實(shí)例是需要調(diào)用實(shí)例的構(gòu)造函數(shù)的,很顯然容器默認(rèn)的為我們調(diào)用了構(gòu)造函數(shù),倘若構(gòu)造函數(shù)帶有參數(shù),則容器則會(huì)創(chuàng)建相應(yīng)的參數(shù)實(shí)例?,F(xiàn)在問(wèn)題來(lái)了,假如我的參數(shù)是一個(gè)接口或者抽象類(lèi)型怎么辦? 很顯然要能創(chuàng)建這樣的參數(shù)我們就必須知道參數(shù)的映射類(lèi)型,?看如下例子,我們?cè)贗Service項(xiàng)目中重新創(chuàng)建一個(gè)接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace IService { public interface ISay { string Say(); } } View Code我們寫(xiě)一個(gè)服務(wù)實(shí)現(xiàn)該接口
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 version="1.0" encoding="utf-8"?><!--有關(guān)如何配置 ASP.NET 應(yīng)用程序的詳細(xì)消息,請(qǐng)?jiān)L問(wèn)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><register name="english" type="IService.ISayHello,IService" mapTo="Service.EnglishSayHello,Service"></register><register type="IService.ISay,IService" mapTo="Service.ComSayHello_V2,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.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.GetService<ISayHello>();//ISayHello sayhello1 = ServiceLocator.Instance.GetService<ISayHello>("english");//showInfo.InnerText = string.Format("{0}+{1}", sayhello1.hello(), sayhello.hello());//showInfo.InnerText = sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();ISay say = ServiceLocator.Instance.GetService<ISay>();showInfo.InnerText=say.Say();}} } View Code我們得到結(jié)果:你好。在配置文件中我們添加了兩個(gè)注冊(cè)節(jié)點(diǎn),從結(jié)果中我們看見(jiàn),容器默認(rèn)選擇了未命名的節(jié)點(diǎn),倘若我們注釋該節(jié)點(diǎn)程序?qū)?bào)錯(cuò),程序沒(méi)辦法自動(dòng)識(shí)別帶名稱(chēng)的節(jié)點(diǎn),要想讓程序識(shí)別帶名稱(chēng)的節(jié)點(diǎn)我們需要配置構(gòu)造函數(shù)參數(shù),配置如下
<?xml version="1.0" encoding="utf-8"?><!--有關(guān)如何配置 ASP.NET 應(yīng)用程序的詳細(xì)消息,請(qǐng)?jiān)L問(wèn)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>--><register name="english" type="IService.ISayHello,IService" mapTo="Service.EnglishSayHello,Service"><!--<lifetime type="singleton"/>--></register><register type="IService.ISay,IService" mapTo="Service.ComSayHello_V2,Service"><constructor><param name="say" dependencyName="english"></param></constructor></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web> </configuration> View Code結(jié)果正確的顯示為:hello,在這里順便提一下如果我們?nèi)∠鹐nglish注冊(cè)節(jié)點(diǎn)lifetime的注釋,我們會(huì)發(fā)現(xiàn)每次創(chuàng)建ComSayHello_V2的參數(shù)將來(lái)自同一個(gè)實(shí)例的引用,原因請(qǐng)參見(jiàn),上文的實(shí)例生命周期。
??? 當(dāng)然我們也可以直接在配置文件的構(gòu)造函數(shù)中指定,參數(shù)類(lèi)型而避免注冊(cè)其他類(lèi)型節(jié)點(diǎn),配置文件代碼如下,結(jié)果一樣
?? ?其實(shí),我們還能夠在配置文件中給參數(shù)賦值,但是如果參數(shù)是一個(gè)復(fù)雜類(lèi)型,比如類(lèi)的時(shí)候,我們就需要一個(gè)轉(zhuǎn)換器,把字符串類(lèi)型的值轉(zhuǎn)換為指定的賦值類(lèi)型,因?yàn)樵谂渲梦募形覀冑x值的類(lèi)型只能是string。轉(zhuǎn)換器在平時(shí)實(shí)踐中用的少,所以我不打算多說(shuō)。需要注意的是,如果我們的類(lèi)中有多個(gè)構(gòu)造函數(shù)的話(huà),那么容器默認(rèn)總會(huì)選擇參數(shù)最多的那個(gè)構(gòu)造函數(shù)。
?? 以上所介紹的歸根到底也只是一種構(gòu)造函數(shù)注入。其實(shí)Unity還提供能屬性注入與方法注入,即在創(chuàng)建實(shí)例的時(shí)候動(dòng)態(tài)為某個(gè)屬性賦值或者調(diào)用某個(gè)方法,其實(shí)這個(gè)要做到也蠻簡(jiǎn)單的,我們只需要在相應(yīng)的屬性上面打上[Dependency]特性,在方法上打上[InjectionMethod]特性即可,但是這兩種方式對(duì)類(lèi)的侵入性太強(qiáng),不推薦使用
總結(jié)
??? 本文簡(jiǎn)單的演示了Unity IOC的一些使用方法,因?yàn)樵谖业目蚣苤?#xff0c;Unity在層次解耦中充當(dāng)了重要的作用,除此之外Unity其實(shí)還能實(shí)現(xiàn)的AOP攔截,但是由于篇幅的原因不再多講,在這里要提醒大家務(wù)必理解實(shí)體的生命周期,因?yàn)檫@對(duì)實(shí)現(xiàn)單元工作模式有著重要的意義。在我的系列前三篇中,我都是采用了是反射來(lái)解耦,有興趣的朋友可以嘗試下用Unity取代它。我目前寫(xiě)的案例與前面系列的版本框架有很大的差異,所以有些知識(shí)點(diǎn)必須和大家說(shuō)明,相信在接下來(lái)的一到兩篇中,就能與大伙見(jiàn)面,祝大伙周末愉快。本篇測(cè)試源碼請(qǐng)點(diǎn)擊這里
?
轉(zhuǎn)載于:https://www.cnblogs.com/shaoshun/p/3868968.html
總結(jié)
以上是生活随笔為你收集整理的企业级应用框架(五)IOC容器在框架中的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: PMP工具与技术篇--4.2.1-2 决
- 下一篇: POJ 2152 Fire