在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;
}