在Suteki.Shop中,未使用微軟自已的Unity框架來實現IOC,而是使用了大名鼎鼎Castle Windsor。
因為引用了Windsor,就有必要簡要介紹一下。而我的理解,這個IOC容器(Container)包括下 面幾個重要概念:
容器(Container):Windsor是一個反轉控制容器。它創建在一個微內核的基 礎之上,這個微內核能夠掃描類並且試圖找到這些類用到哪些對象引用、對象依賴,然後把這些依賴信 息提供給類使用。
組件(Component):也就是我們通常所說的業務邏輯單元及相應的功能實現 ,組件是一個可復用的代碼單元。它應該實現並暴露為一個服務。組件是實現一個服務或接口的類。
服務(Service) :也就是相應的組件接口或N個Component按業務邏輯組合而成的業務邏輯接口 。
接口是服務的規范,它創建一個抽象層,你可以輕松的替換服務的實現。
擴張單元插 件(Facilities):提供(可擴張)容器以管理組件。
我們可以直接使用組件(會在下 面的內容中提到),也可以把組件轉換成相應的服務接口來使用。
還記得上一篇文章中提到的 Service嗎? 說白了,它就是一個服務。而Suteki.Shop做的更“誇張”,只要是帶有業務邏 輯性質的功能代碼都可以被視為Component或服務,比如說前幾篇文章中所提到的Filter,ModelBinder。 甚至是服務組件初始化的輔助類(WindsorServiceLocator)也一並拿下。
為了便於理解,下面 就到Suteki.Shop中看一下其是如何做的:)
首先我們看一下整個Suteki.Shop項目啟動的入口,同 時這也是Windsor IOC容器初始化的起點。而這塊功能代碼是放在了Global.asax (Suteki.Shop\Global.asax)中的Application_Start方法中實現的,下面是該方法的聲明:
protected void Application_Start(object sender, EventArgs e)
{
RouteManager.RegisterRoutes(RouteTable.Routes);
InitializeWindsor();
}
代碼中的RouteManager.RegisterRoutes是實現對Route規則的綁定,而規則的內容是被 硬編碼到RouteManager中實現的。關於Route的資料網上有不少,園子裡也有不少朋友寫過,這裡就不做 說明了。
接就上面方法就會運行InitializeWindsor(),這就是Windsor容器初始化的方法:
/// <summary>
/// This web application uses the Castle Project's IoC container, Windsor see:
/// http://www.castleproject.org/container/index.html
/// </summary>
protected virtual void InitializeWindsor()
{
if (container == null)
{
// create a new Windsor Container
container = ContainerBuilder.Build ("Configuration\\Windsor.config");
WcfConfiguration.ConfigureContainer(container);
ServiceLocator.SetLocatorProvider(() => container.Resolve<IServiceLocator> ());
// set the controller factory to the Windsor controller factory (in MVC Contrib)
System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
}
}
注: “Configuration\\Windsor.config”中的內容較長,主要是一些XML配置節點。大家可以抽 時間閱讀一下即可。
這個方法是今天講解的主要內容,下面就介紹一下其中的代碼。
首 先是判斷container(IWindsorContainer類型)是否為空,如果容器為空則創建並初始化該容器。也就 是調用ContainerBuilder(Suteki.Shop\ContainerBuilder)類的Build方法來從外部的config文件中加 載默認信息。我們這裡就看一下Build方法的實現:
public static IWindsorContainer Build(string configPath)
{
var container = new WindsorContainer(new XmlInterpreter(configPath));
// register handler selectors
container.Kernel.AddHandlerSelector(new UrlBasedComponentSelector(
typeof (IBaseControllerService),
typeof(IImageFileService),
typeof (IConnectionStringProvider)
));
// automatically register controllers
container.Register(AllTypes
.Of<Controller>()
.FromAssembly (Assembly.GetExecutingAssembly())
.Configure(c => c.LifeStyle.Transient.Named (c.Implementation.Name.ToLower())));
container.Register(
Component.For<IUnitOfWorkManager> ().ImplementedBy<LinqToSqlUnitOfWorkManager>().LifeStyle.Transient,
Component.For<IFormsAuthentication> ().ImplementedBy<FormsAuthenticationWrapper>(),
Component.For<IServiceLocator>().Instance(new WindsorServiceLocator(container)),
Component.For<AuthenticateFilter>().LifeStyle.Transient,
Component.For<UnitOfWorkFilter>().LifeStyle.Transient,
Component.For<DataBinder>().LifeStyle.Transient,
Component.For<LoadUsingFilter>().LifeStyle.Transient,
Component.For<CurrentBasketBinder>().LifeStyle.Transient,
Component.For<ProductBinder>().LifeStyle.Transient,
Component.For<OrderBinder>().LifeStyle.Transient,
Component.For<IOrderSearchService>().ImplementedBy<OrderSearchService> ().LifeStyle.Transient,
Component.For<IEmailBuilder> ().ImplementedBy<EmailBuilder>().LifeStyle.Singleton
);
return container;
}