IoC - Castle Windsor 2.1

 
 
基本示例
项目要引用Castle.Core.dll、Castle.MicroKernel.dll、Castle.Windsor.dll,引用namespace:
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
假设有一个服务类TaxCalculator,用来计算税额:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
public  class  TaxCalculator
{
     private  decimal  _rate = 0.125M;
     public  decimal  Rate
     {
         set  { _rate = value; }
         get  { return  _rate; }
     }
     public  decimal  CalculateTax( decimal  gross)
     {
         return  Math.Round(_rate * gross, 2);
     }
}
计算税额时的代码如下:
?
1
2
3
4
5
6
WindsorContainer container = new  WindsorContainer( new  XmlInterpreter());
TaxCalculator calculator = container.Resolve<TaxCalculator>();
decimal  gross = 100;
decimal  tax = calculator.CalculateTax(gross);
Console.WriteLine( "Gross: {0}, Tax: {1}" , gross, tax);
Console.ReadKey();
app.config中的配置如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
< configuration >
     < configSections >
         < section  name = "castle"
             type = "Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"  />
     </ configSections >
< br >
     < castle >
         < components >
             < component  id = "taxcalc.service"  type = "Windsor.Test.TaxCalculator, Windsor.Test" >
             </ component >
         </ components >
     </ castle >
</ configuration >
运行程序,计算结果为12.5。可以在配置文件中为Rate属性指定其他值,例如:
?
1
2
3
4
5
< component  id = "taxcalc.service"  type = "Windsor.Test.TaxCalculator, Windsor.Test" >
     < parameters >
         < Rate >0.25</ Rate >
     </ parameters >
</ component >
上面的配置指定税率为0.25,因此计算结果为25

配置
如果在Windsor容器创建对象实例时,需要注入的属性为数组,而不是上面Rate这样的单个值,怎么配置?
假如要注入的属性为:
?
1
2
3
4
5
public  DateTime[] Holidays
{
     get  { return  _holidays; }
     set  { _holidays = value; }
}
则可以如下配置:
?
1
2
3
4
5
6
7
8
9
10
11
< component  id = "holidays.service"  type = "Windsor.Test.HolidayService, Windsor.Test"  >
     < parameters >
         < Holidays >
             < array >
                 < item >2007-12-24</ item >
                 < item >2007-12-25</ item >
                 < item >2008-1-1</ item >
             </ array >
         </ Holidays >
     </ parameters >
</ component >
如果要注入的属性为Dictionary类型,例如:
?
1
2
3
4
5
public  Dictionary< string , string > Aliases
{
     get  { return  _aliases; }
     set  { _aliases = value; }
}
配置如下:
?
1
2
3
4
5
6
7
8
9
10
11
< component  id = "aliases.service"  type = "Windsor.Test.HolidayService, Windsor.Test" >
     < parameters >
         < Aliases >
             < dictionary >
                 < entry  key = "dog" >duck</ entry >
                 < entry  key = "ate" >broke</ entry >
                 < entry  key = "homework" >code</ entry >
             </ dictionary >
         </ Aliases >
     </ parameters >
</ component >
注入的数组、Dictionary属性,我们都不需要初始化创建这个对象,Windsor在注入的时候会新建数组或者Dictionary对象设置给相应属性
假如,现在通过Windsor配置的服务类比较多,我建立了2份配置,一份用于测试,一份用于生产环境,如何方便的在这2份配置之间切换呢?可以在配置文件中使用include实现, 示例如下:
?
1
2
3
4
< castle >
     <!--<include uri="file://container-debug.config" />-->
     < include  uri = "file://container-live.config"  />
</ castle >
include甚至可以包含assembly中的resource(嵌入assembly中的文件)
另外可以在配置文件中定义属性,然后在其他地方引用这些属性,例如定义属性:
?
1
2
3
4
5
< configuration >
   < properties >
     < myProperty >Live</ myProperty >
   </ properties >
</ configuration >
使用属性:
?
1
2
3
4
5
< component  id = "whatConfig.service"  type = "Windsor.Test.HolidayService, Windsor.Test" >
     < parameters >
         < Configuration >#{myProperty}</ Configuration >
     </ parameters >
</ component >
我们可以针对同一个服务配置多个实现方式,使用id获取各个实现方式的对象实例:
?
1
2
3
4
5
6
7
8
9
10
< component  id = "reader.file1"  type = "IoC.Tutorials.Part8.FileReader, IoC.Tutorials.Part8" >
     < parameters >
         < FileName >file1.txt</ FileName >
     </ parameters >
</ component >
< component  id = "reader.file2"  type = "IoC.Tutorials.Part8.FileReader, IoC.Tutorials.Part8" >
     < parameters >
         < FileName >file2.txt</ FileName >
     </ parameters >
</ component >
然后使用配置中的id来获取实例对象:
?
1
2
3
4
WindsorContainer container = new  WindsorContainer( new  XmlInterpreter());
FileReader defaultReader = container.Resolve<FileReader>();
FileReader file1Reader = container.Resolve<FileReader>( "reader.file1" );
FileReader file2Reader = container.Resolve<FileReader>( "reader.file2" );
我们可以使用container.Kernel.HasComponent(string key)方法在代码中判断特定的key是否有注册了服务

生命周期 Lifestyle, Lifecycle
Windsor容器中的对象其生命周期有以下几种方式:
Singleton: 单例模式
Transient: 临时对象模式,每次都创建一个新对象返回给请求者
PerThread: 在当前执行线程上为单例模式
Pooled: 用一个对象池管理请求对象,从对象池中返回对象实例
Custom: 实现Castle.MicroKernel.Lifestyle.ILifestyleManager或从Castle.MicroKernel.Lifestyle.AbstractLifestyleManager继承,实现自定义的对象生命周期管理
默认情况下,组件的生命周期为单例模式,可以通过配置文件进行设置:
?
1
<component id= "taxcalc.service"  type= "Windsor.Test.TaxCalculator, Windsor.Test"  lifestyle= "transient"  />
也可以给class添加上[Castle.Core.Transient]、[Castle.Core.PerThread]等属性来设置组件的生命周期,从而忽略配置文件中的设置
Windsor支持Castle.Core.IInitializable和System.IDisposable接口,如果类实现了IInitializable接口,容器在创建对象实例之后会执行接口的Initialize方法;如果类实现了IDisposable接口,则在销毁对象的时候会执行Dispose方法

构造器注入
前面示例我们用的都是setter注入,下面示例使用构造器注入
有一个用于字符串编码的接口,该接口有2个实现:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  interface  IEncoder
{
string  Encode( string  source);
}
public  class  NullEncoder : IEncoder
{
public  string  Encode( string  source)
{
return  source;
}
}
public  class  SillyEncoder : IEncoder
{
     private  char [] _mixedUp = "YACBDFEGIHJLKMONPRSQTUWVXZ" .ToCharArray();
<br>
     public  string  Encode( string  source)
     {
         string  upperSource = source.ToUpper();
         char [] encoded = new  char [source.Length];
         for  ( int  i = 0; i < encoded.Length; i++)
         {
             encoded[i] = MapCharacter(upperSource[i]);
         }
         return  new  string (encoded);
     }
<br>
     private  char  MapCharacter( char  ch)
     {
         if  ((ch >= 'A' ) && (ch <= 'Z' ))
         {
             return  _mixedUp[ch - 'A' ];
         }
         return  ch;
     }
}
然后有一个发送消息的类,其构造函数要求一个IEncode对象:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  MessageSender
{
     private  readonly  IEncoder _encoder;
     private  readonly  string  _from;
     public  MessageSender( string  from, IEncoder encoder)
     {
         _from = from;
         _encoder = encoder;
     }
     public  void  SendMessage( string  to, string  body)
     {
         Console.WriteLine( "to: {0}\r\nfrom: {1}\r\n\r\n{2}" , to, _from, _encoder.Encode(body));
     }
}
使用Windsor可以实现:Windsor自动创建一个IEncoder对象提供给MessageSender的构造函数;在配置文件中需要指定from参数的值,否则Windsor将抛出异常无法创 建MessageSender对象
使用的代码如下:
?
1
2
3
4
WindsorContainer container = new  WindsorContainer( new  XmlInterpreter());
MessageSender sender = container.Resolve<MessageSender>();
sender.SendMessage( "hammet" , "castle is great!" );
Console.ReadKey();
配置如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
< component  id = "encoder.silly"
     service = "Windsor.Test.IEncoder, Windsor.Test"
     type = "Windsor.Test.SillyEncoder, Windsor.Test"  />
< component  id = "encoder.null"
     service = "Windsor.Test.IEncoder, Windsor.Test"
     type = "Windsor.Test.NullEncoder, Windsor.Test"  />
< component  id = "messageSender"
     type = "Windsor.Test.MessageSender, Windsor.Test" >
     < parameters >MessageSender
         < from >[email protected]</ from >
     </ parameters >
</ component >
上面我们有2个IEncoder的实现,我们可以在配置文件中为MessageSender的构造函数指定使用哪一个实现类:
?
1
2
3
4
5
6
7
< component  id = "messageSender"
     type = "Windsor.Test.MessageSender, Windsor.Test" >
     < parameters >MessageSender
         < from >[email protected]</ from >
         < encoder >${encoder.null}</ encoder >
     </ parameters >
</ component >
Factory Facilities
我们自己写的类完全由我们自己控制,因此我们可以让他们能够通过Windsor容器管理,但对于某些第三方提供的服务程序,可能构造函数存在额外的依赖性,使得我们无法通过配置直接使用Windsor容器来管理,这种情况下可以使用Windsor的Factory Facilities实现一个工厂,告诉Windsor使用我们的工厂来创建特定的服务对象实例
比如我们实现了下面这样一个工厂类,用来创建一个ISmsService服务对象:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public  class  SmsServiceFactory
{
     private  string  _userName;
     private  string  _password;
     private  int  _retryAttempts = 3;
<br>
     public  SmsServiceFactory( string  userName, string  password)
     {
         _userName = userName;
         _password = password;
     }
     public  int  RetryAttempts
     {
         get  { return  _retryAttempts; }
         set  { _retryAttempts = value; }
     }
<br>
     public  ISmsService CreateService()
     {
         SmsService service = new  SmsService();
         SmsService.SmsConfig config = new  SmsService.SmsConfig();
         config.SetCredentials(_userName, _password);
         config.RetryAttempts = _retryAttempts;
         service.SetConfig(config);
         return  service;
     }
}
然后我们使用下面的配置,通过Windsor Factory Facilities指定我们所使用的工厂类:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
< castle >
     < facilities >
         < facility
             id = "factorysupport"
             type = "Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"  />
     </ facilities >
     < components >
         < component  id = "smsService.Factory"
             type = "Windsor.Test.SmsServiceFactory, Windsor.Test" >
             < parameters >
                 < userName >joe</ userName >
                 < password >secret</ password >
             </ parameters >
         </ component >
         < component  id = "smsService.default"
             type = "Windsor.Test.ISmsService, Windsor.Test"
             factoryId = "smsService.Factory"
             factoryCreate = "CreateService"  />
     </ components >
</ castle >
使用的代码跟其他示例一样:
1
2
3
WindsorContainer container = new  WindsorContainer( new  XmlInterpreter());
ISmsService smsService = container.Resolve<ISmsService>();
smsService.SendMessage( "+465556555" , "testing testing...1.2.3" );
 
 
 

你可能感兴趣的:(cast)