依赖注入容器之Castle Windsor

一.Windsor的使用

Windsor的作为依赖注入的容器的一种,使用起来比较方便,我们直接在Nuget中添加Castle Windsor,将会自动引入Castle.Core 和 Castle.Windsor,就可以正常使用。

1.逐个组件进行注册

使用注册模块中的Component

 IWindsorContainer container = new WindsorContainer();
            container.Register(Component.For<IAppleService>() //接口
    .ImplementedBy<AppleService>()); 

 在这里取一个特殊的例子,我们注册一个带有参数的的实例,我们直接使用Resolve实现兑现的注入

    public class AppleService : IAppleService
    {
        public AppleService(decimal price)
        {
            this.Price = price;
        }
        public decimal Price { get; set ; }
        public void Output()
        {
            Console.WriteLine(Price);
        }
    }
//具体的实现注入
 var apple= container.Resolve<IAppleService>(new Dictionary<string,decimal>() { { "price",222.22m} });
            apple.Output();
            Console.ReadLine();

在Resolve方法中提供了一个IDictionary的参数,所以我们可以通过Dictionary或者HashTable将参数传递进去。

单个组件进行注册,当我们项目中的类较少的时候还是可以的,但是我们在实际的项目中,往往都是遵循单一化的职责,创建出大量的文件,采用上面的方式将会变成一种灾难。

2.通过规则批量注册(通过类库注册)

  container.Register(Classes.FromThisAssembly()   //当前程序集,也可以用FromAssemblyInDirectory()等
                    .InSameNamespaceAs<CatService>() //与CatService类具有相同的命名空间
                   // .InNamespace("Windsor")//直接指定命名空间
                     .WithService.DefaultInterfaces()
                     .LifestyleTransient());    //临时生命周期
            var apple= container.Resolve<IAppleService>(new Dictionary<string,decimal>() { { "price",222.22m} });
            var cat = container.Resolve<ICatService>();
            cat.Eat();
            apple.Output();

 详细的API可以通过BasedOnDescriptor中的详细说明机型使用

3.通过安装器Installer进新注册

因为测试代码是使用控制台程序进行测试的,上面的两种方式我们把注册的代码直接写在Main方法中,这样的方式耦合性高,同时不方便管理。所以可以采用安装器的方式进行注册

(1)通过接口IWindsorInstaller实现安装器

//定义
 public class ServiceInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Classes.FromThisAssembly()   //当前程序集,也可以用FromAssemblyInDirectory()等
                   .InSameNamespaceAs<CatService>() //与CatService类具有相同的命名空间
                   .WithServiceDefaultInterfaces()//注册所有的默认的接口和实现类
                    .LifestyleTransient());    //临时生命周期
        }
    }

//注册
    //3.通过安装器进行注册
            container.Install(FromAssembly.Named("Windsor"));
            var apple= container.Resolve<IAppleService>(new Dictionary<string,decimal>() { { "price",222.22m} });
            var cat = container.Resolve<ICatService>();
            cat.Eat();
            apple.Output();

 

上面通过安装器进注册的时,指定了程序集Windor下面的安装器将会被调用,那么此时有一个问题,如果我的安装器定义在当前类库的文件夹下面是否可以被注册使用呢?

通过测试发现,是可以被注册的。

另一个问题,如果我们的安装器中注册的类出现重复的,那么我们的容器中会不会重复注册?

通过测试发现,不会被重复注册,也不会报错哦。

还有一个问题

会不会报错呢?

答案是不会的,这个可以从CastleWindsor的源码中可以找到,这是有它的的实现机制决定的,会判断重复,维护这一个Dictionary

(2)通过xml配置文件

//使用配置文件  container.Install(Configuration.FromXmlFile("WindsorSetting.xml"));
            var cat = container.Resolve<ICatService>();
            cat.Eat();
            Console.ReadLine();
//定义配置文件WindsorSetting.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <installers>
    <install type="Windsor.ServiceInstaller,Windsor"/>
   
  </installers>
  <!--<components>
    <component type="Windsor.CatService,Windsor" service="Windsor.ICatService,Windsor">
      
    </component>
  </components>-->
</configuration>

 

 type中的字符串,第一个表示的是命名空间下的具体的安装器类,第二个表示程序集名称

上面的代码中我们自定义了xml文件,当然我们可以直接使用项目中app.config

//调用 
container.Install(Configuration.FromAppConfig());
            var cat = container.Resolve<ICatService>();
            cat.Eat();
//配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
  </configSections>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>

  <castle>
    <installers>
      <install type="Windsor.ServiceInstaller,Windsor"/>
      <!--查找该程序集下所有IWindsorInstaller接口的类型进行注册-->
      <!--<install assembly="WindsorInstaller"></install>-->

      <!--查找dll文件-->
      <!--<install directory="Extensions" fileMask="*.dll"></install>-->
    </installers>
  </castle>
</configuration>

 二.Windsor中的InstallerFactory的使用

从上面的随笔中我们可以了解到常用的windsor的注入使用的方式,那么其实还有一个很现实的问题,上面的实现方式都是没有注入顺序的,也就是说如果类1用到了另一个注入容器的类2,那么倘若类2如果在类1之后注册,那么将会报错,找不到对应的实例。其实解决这个问题也是好办的,只要我们使用Component一个类一个类的注册就OK了。其实windsor中还提供了另外的方式,那就是InstallerFactory他可以控制注入的顺序。先上代码

 public class ServiceFactory:InstallerFactory
    {
        public override IEnumerable<Type> Select(IEnumerable<Type> installerTypes)
        {
            var retval = installerTypes.OrderBy(x => this.GetPriority(x));
            return retval;
        }

        private int GetPriority(Type type)
        {
            var attribute = type.GetCustomAttributes(typeof(InstallerPriorityAttribute), false).FirstOrDefault() as InstallerPriorityAttribute;
            return attribute != null ? attribute.Priority : InstallerPriorityAttribute.DefaultPriority;
        }
    }
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class InstallerPriorityAttribute : Attribute
    {
        public const int DefaultPriority = 100;

        public int Priority { get; private set; }
        public InstallerPriorityAttribute(int priority)
        {
            this.Priority = priority;
        }
    }
}
//在Program中注册
  container.Install(FromAssembly.This(new ServiceFactory()));

 

 其实在Factory中的Select的方法中会将所有的继承自IWindsorInstaller的类的类型获取到,然后我们通过特性标签的方式对Installer进行排序,这样我们就可以控制Installer被初始化的顺序了。

三.Windsor的拓展

1.泛型的注入

 container.Register( Component.For(typeof(ICatService<>)).ImplementedBy(typeof(CatService<>)); 

2.注册顺序的简单设置

 // 取先注册的
            container.Register(
                Component.For<ICatService>().ImplementedBy<CatService>(),
                Component.For<IAppleService>().ImplementedBy<AppleService>()
            );

           // 强制取后注册的
            container.Register(
                Component.For<ICatService>().ImplementedBy<CatService>(),
                Component.For<IAppleService>().Named("SelfDefinedService").ImplementedBy<AppleService>().IsDefault()
            );

3.直接注册实例对象

 var cat = new CatService();
container.Register(
                Component.For<ICatService>().Instance(cat)
                );

 

posted @ 2018-05-04 13:12  善良的小赵  阅读(749)  评论(0编辑  收藏  举报