现在的位置: 首页 > 综合 > 正文

Castle实践-开篇

2013年02月03日 ⁄ 综合 ⁄ 共 12198字 ⁄ 字号 评论关闭

1关于Castle
IOC
容器:

●Castle Windsor
Windsor
CastleIOC容器(built
on top of a MicroKernel)
,包含几个概念:
组件(Component
服务(Service)
扩张单元插件(Facilities
我是这样理解他们之间的关系的:组件提供服务,也就是服务是一个个接口,而Facilities提供扩张容器管理组件的能力。我们可以直接使用组件,也可以把组件转换成相应的服务接口来使用。
也可以这么说,Component是普通的组件,Facilities是带有注入性质的组件。
自动装配(Auto-wiring
自动装配的意思是指由容器自动管理组件之间的依赖关系,而无需编写特定的xml config来配置依赖关系,springcastle都支持自动装配,但是spring文档中是不推荐使用自动装配的,castle本身就是自动装配的,这是他们之间一个很大的区别。
spring
不推荐自动装配的原因是:手动控制依赖关系让你自知道自己在做什么,有利于规范文档。
castle作者是这样认为的:不使用自动装配,配置文档极之冗长,当组件配置到一定数目时候,管理起来就非常困难。
对比之下,如果我们要改变一个依赖关系或者增加一个组件依赖,使用Castle是比Spring容易得多的。
扩张单元(Facilities
如果你想扩张容器的功能,你可以通过创建扩张单元达到目的。
Castle
提供有一系列的扩张单元,你也可以建立自己的扩展单元。这些扩张单元是可以重用的,你可以在扩张单元里面订阅容器事件,给组件附加属性,建立拦截器,控制组件生命周期等。
spring里面,如果要拓展容器的功能,相信就要实现自己的容器,比如从XmlObjectFactory继承下来,相比之下,Castle是通过一种插件的形式达到目的,优势是明显的。
关于Castle提供的Facilities请参见:
http://www.castleproject.org/index.php/Facilities
2
关于CastleAOP
AspectSharpCastle提供的AOP轻量级框架,AspectSharp是建立在DynamicProxy的基础上,同时Castle提供了AspectSharpFacilities使你可以更容易的在IOC容器里面应用Aspect#
3
Castle Project ActiveRecord
如果你的项目中使用Nhnbernate作为数据库持久层,有了这个你就可以废弃掉hbm.xml文件了,activeRecord通过Attribute来配置持久类,而且配合ActiveRecord
Generater
可以很方便的生成,管理持久类代码,而不用去找生成机的烦恼。我原本是用CodeSmith写了一个Nhibernate的模板,看来可以丢掉了,不过目前我对ActiveRecord没有更多的尝试。
4
Castle
Project DynamicProxy

实现动态代理,在Java里面有专门的库来实现代理,而。net的却没有动态代理的相关库,Castle
DynamicProxy
来弥补这个不足,他是基于Emit技术的。写本文为止的版本是1.1.0.0,算是很成熟的了。而且AOPNhibernateIBatis都有用到DynamicProxy,了解他是非常重要的,网络上已经有很多介绍DynamicProxy的文章,我会转载在论坛上作为资料收集,了解他是你进行对其他技术研究的基础。
5
Castle
Project MonoRail

MonoRail原名叫Castle on Rails,他涉及到“Action
Pack”
的概念,具体请参见:http://ap.rubyonrails.org/MonoRail
is an attempt to provide a port of Action Pack for .Net. The Action Pack way of development is extremelly productive, very intuitive and easily testable.

Castle实践1Castle
IOC
容器剖析

大家好!本节向大家讲述IOC容器的基本使用方法并来看看他是如何处理的。
     IOC
的概念我就不讲了,如果你对这还不够熟悉的话,我推荐你看Inversion
of Control Containers and the Dependency Injection pattern
。我这里借助hammett的一句话来概括IOC的意义:
one of the uses of inversion of control that solves the loosely coupled problem (or one aspect of it) is by inverting how your class obtains external object references or configuration.
用两个字概括-低耦。



1
)获取容器
Windsor作为Castle使用IOC容器是建立在MicroKernel的基础上的,用于负责检测类型和类型之间工作依赖性,并提供服务(requirements)或者发生错误时提供预警的机制(fail
fast
)。

// 建立一个容器
IWindsorContainer container = new WindsorContainer();
// 加入一个组件
container.AddComponent( "newService", typeof(IService), 
                       typeof(MyService) );
// 获取组件
IService service = (IService)container["newService"];
MyService service = (MyService)container["newService"];
// 使用
service.DoSomeService();

 

上面演示了使用容器的一个简单过程:
首先,我们向容器注册了一个服务接口IService,而MyService是实现了这个接口的一个类,newService是一个key用来标识这个组件,然后通过key,我们可以取得注册的组件,可以直接转为IService接口或者是MyService来使用。
在这里,如果这个Service组件是依赖于其他其他组件的,在其他组件没有加载,换句话就是说依赖性检查尚未通过时,就使用的话,会产生Exception的。只有使用的组件的所有依赖性完全满足时,才可以正常使用。
深入分析组件的注册:
Windsor
的核心是MicroKernel,组件是交由MicroKernelModelBuilder处理的。
找到DefaultComponentModelBuilder类,它实现了IComponentModelBuilder接口,提供默认的组件向容器注册的行为。
IComponentModelBuilder
有三个方法BuildModelAddContributorRemoveContributor,其中BuildModel按顺序调用Add进来的Contributor对组件进行处理。
所有的Contributor都实现IComponentModelBuilder接口,这个接口只有ProcessModel一个方法,从方法名字中可以很清楚的知道这个接口就是用于处理模块的。
看回DefaultComponentModelBuilder的初始化方法:InitializeContributors(),这里挂接各种Contributor来形成一个处理流来处理组件模块。

 

protectedvirtualvoid InitializeContributors()
{
   // 处理配置
   AddContributor( new ConfigurationModelInspector() );
   // 处理生命方式:Undefined,Singleton,Thread,Transient,Pooled,Custom
   AddContributor( new LifestyleModelInspector() );
   // 处理构造函数依赖
   AddContributor( new ConstructorDependenciesModelInspector() );
   // 处理属性依赖
   AddContributor( new PropertiesDependenciesModelInspector() );
   // 处理生命周期,也就是在组件装在,初始化,销毁所出发的行为,分别对应三个接口:IInitializable,ISupportInitialize,IDisposable。你的组件实现了这些接口,容器会自动在不同的生命周期调用他们。
   AddContributor( new LifecycleModelInspector() );
   // 处理配置文件中的parameters元素内容
   AddContributor( new ConfigurationParametersInspector() );
   // 处理配置文件中的interceptors元素内容
   AddContributor( new InterceptorInspector() );
}

 

最后,你可以通过AddContributor或者RemoveContributor来新添对组件的个性化处理。
忘记说了,注册组件的过程就是建立ComponentModel的过程,ComponentModel是组件的信息库
完成注册组件之后,就可以使用组件了,组件由容器创建,而这个创建工作交给ComponentActivator处理,Activator会根据ComponentModel里面的信息对组件进行依赖检查,依赖检查通过后,根据指定的生命方式创建组件(默认是单例),并在创建的时候触发实现了IInitializableISupportInitialize的组件行为。对象创建之后就直到销毁,这时候对象实现的IDisposable就被调用了。

2)配置容器
在上面我们获取容器时,是直接将容器实例化的。
IWindsorContainer container = new WindsorContainer();
那么我们如何配置容器呢?
其实直接将容器实例化的时候,容器会自动检查你的AppConfig中的容器配置,如下:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration>
   <configSections>
       <section name="castle"
         type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, 
               Castle.Windsor"/>
   </configSections>
   <castle>
       <components>
           <component id="smtpemailsender">
               <parameters>
                   <host>localhost</host>
                   <port>110</port>
               </parameters>
           </component>
       </components>
   </castle>
</configuration>

 

当然,你也可以通过其他方式来配置容器:
*
指定Xml文件,传入构造器。
*
实现接口:IConfigurationStore来自定义配置。
配置容器莫非就是做了两件事情:
一是注册Facility,二是注册Component
WindsorContainer
MicroKernel的外壳,WindsorContainer包含3个内部属性:

private IKernel _kernel;
private IWindsorContainer _parent;
private IComponentsInstaller _installer;

 

WindsorContainer提供对_kernel的初始化,WindsorContainer的配置能力很强,可以实现自己的配置解释语言,也有提供默认的XML的方式:
IConfigurationInterpreter
是一个配置解释器接口,它只有一个方法:
void Process(IConfigurationStore store);
方法名称表明是处理IConfigurationStore,而IConfigurationStore提供给_kernel内部使用,也就是_kernel通过此接口获取组件和扩展单元的配置。下面分析一下WindsorContainer处理配置的过程:
//
通过传入xml文件路径来初始化配置
IWindsorContainer container = new WindsorContainer("../MyConfig.xml");
内部是这样处理的:
开始传入xml

public WindsorContainer(String xmlFile) :this(new XmlInterpreter(xmlFile)) {}

 


XmlInterpreter
是一个IConfigurationInterpreter,接着来看下一步:

public WindsorContainer(IConfigurationInterpreter interpreter) :this()
{
if (interpreter ==null)thrownew ArgumentNullException("interpreter");
interpreter.Process(Kernel.ConfigurationStore);
RunInstaller();
}

 


再下一步:

public WindsorContainer() :this(new DefaultKernel(), new Installer.DefaultComponentInstaller()) {}

 


最后一步:

public WindsorContainer(IKernel kernel, IComponentsInstaller installer)
{
_kernel = kernel;
_kernel.ProxyFactory = new Proxy.DefaultProxyFactory();
_installer = installer;
}

 

跟踪上面的构造过程,可以看到:
_kernel
是一个DefaultKernel
_installer
是一个DefaultComponentInstaller
_parent
是当前容器的父容器
在数据初始化完毕后,调用IConfigurationInterpreterProcess来处理Kernel.ConfigurationStore,此时Kernel.ConfigurationStore是一个DefaultConfigurationStore(在new
DefaultKernel()
的时候作为一个SubSystem加入到KernelSubSystem里面),DefaultConfigurationStore里面就只有两个Dictionary分别用来保存facilitiescomponents
当前的的IConfigurationInterpreter是一个XmlInterpreter,跟踪到XmlInterpreterProcess处理:

using (Source)
{
XmlDocument doc = new XmlDocument();
doc.Load(Source.Contents);
Deserialize(doc.DocumentElement, store);
}

 


很明显,这里载入xml文件,根据xml里面的配置反序列化把配置内容加入到DefaultConfigurationStore里面的两个Dictionary里面,这样就完成了facilitiescomponents的初始化了。

3)使用容器
容器的使用都是通过:IWindsorContainer接口的,它提供一系列加载facilitiescomponents的方法。IWindsorContainer把实际的行动交给DefaultKernel处理,而DefaultKernel就调用ComponentModelBuilder来真正BuildModel并引发相应的Event
同时,IWindsorContainer也提供一系列释放facilitiescomponents的方法,释放的同时也要检查依赖性,只有别的组件对要释放的组件没有依赖时候才能成功释放。同样真正的释放动作都是交给Kernel处理的。



今次剖析到此结束,如果大家认为哪里说得不是很清楚的,请提宝贵意见。
建议看本文时候,结合我的思路跟踪下源码,才能真正了解Castle的机制。
下篇,我将带大家进入真正的应用,如何使用Facilities,他和Spring的机制有何不同呢?

Castle实践2Startable
Facility

这一节我们来分析Facility,作为Castle的可扩展单元,他是可以带有注入性的,也就是对组件来说,他可能侵犯组件本身。下面我们从官方提供的一个StartableFacility开始。


先明白这个Facility的作用,也就是要达到的目的:使一个实现了Castle.Model.IStartable接口的程序在满足依赖性的时候,能够自我执行。

publicinterface IStartable
{
    void Start();     // 创建的时候执行
void Stop();      // 销毁的时候执行
}

 


首先我们来了解一下这个接口:ILifecycleConcern,这个接口带有一个方法:

publicinterface ILifecycleConcern
{
    void Apply( ComponentModel model,object component );
}

 


他用来在组件特定的生命周期提供处理行为。

比如,我们上一节提到的,实现了IInitializable接口的组件,容器会自动调用组件的初始化方法Initialize,只是执行这个接口的Facility是内置的(Kernel自带)。

接着,我们来用两个类实现这个接口:

第一个StartConcern,处理:如果是实现IStartable的组件则调用其Start()方法:

publicvoid Apply(ComponentModel model,object component)
{
    (component as IStartable).Start();
}

 


第二个StopConcern,处理:如果实现IStartable的组件则调用其Stop()方法:

publicvoid Apply(ComponentModel model,object component)
{
    (component as IStartable).Stop();
}

 


先别问为什么,知道功能就好,下面会继续讲到。

好了,我们看下如何实现一个Facility,创建一个自定的Facility可以直接实现IFacility接口,或者从AbstractFacility中继承下来,他们的不同支出只是AbstractFacility提供了IFacility接口的默认实现,你只需实现Init()方法即可。但其实IFacility包含的东西并不多,只有两个:

publicinterface IFacility
{
    // Facility被添加到容器中就立刻执行
void Init(IKernel kernel, IConfiguration facilityConfig);
    // 一般在容器的Dispose()中被调用
void Terminate();
}

 


既然我们要达到程序的自启动,好自然,我们应该在组件加入容器的时候检查其依赖性满足后就启动他。那么如何让自定的Facility带有处理组件的能力呢?看下面的代码,在Facility加入容器时候,向容器注册了两个事件:

protectedoverridevoid Init()
{
    // 在组件创建之后引发
    Kernel.ComponentModelCreated +=new ComponentModelDelegate(OnComponentModelCreated);
    // 在组件注册之后引发
    Kernel.ComponentRegistered +=new ComponentDataDelegate(OnComponentRegistered);
}

 

所以,下面的事情就是这样发生的了:

先Add一个组件,ComponentRegistered事件发生了。

在OnComponentRegistered中检查依赖性,如果依赖性满足并组件实现了IStartable的话,则请求创建这个组件。

privatevoid Start(String key)
{
    object instance = Kernel[key];
}

 


创建组件的时候,ComponentModelCreated引发,在ComponentModelCreated中加入生命周期的处理事件,这里就用到了本文开头的两个实现了ILifecycleConcern的类:StartConcern和StopConcern。

把StartConcern注册为组件的LifecycleStepType.Commission(生命开始)周期处理行为。

把StopConcern注册为组件的LifecycleStepType.Decommission(生命结束)周期处理行为。

组件被创建之后,立刻进入LifecycleStepType.Commission周期,StartConcern被触发处理,StartConcern.Apply()调用组件的Start()来达到自启动的目的。

同样组件从容器移除的时候,组件就进入LifecycleStepType.Decommission,那么StopConcern.Apply()触发组件的Stop()来进行“最后的喘息”。

Startable的Facility设计原理已经说完,下步就实践咯。J

///<summary>
///这是一个实现IStartable的类,主要用Application.Run()启动一个窗口
///注意看他的构造函数
///</summary>
publicclass ApplicationRunner : IStartable
{
    private Form _form;
    private Thread _thread;

    // 这里表明:这个类是依赖于类型为Form1的组件的
public ApplicationRunner(Form1 form)
    {
        _form = form;
    }

    #region IStartable成员

    publicvoid Start()
    {
        _thread = new Thread(new ThreadStart(StartApp));
        _thread.Start();
    }

    publicvoid Stop()
    {
        MessageBox.Show("Stop is called, but it do nothing.");
    }

    #endregion

    privatevoid StartApp()
    {
        Application.Run( _form );
    }
}

 


其中Form1是一个普通窗口:

publicclass Form1 : System.Windows.Forms.Form
{
    // ….
}

 


最后是Main:

[MTAThread]
staticvoid Main()
{
     // 建立容器
     IWindsorContainer container =new WindsorContainer("../../AppConfig.xml");

     // 加入Facility
     container.AddFacility("startable",new StartableFacility());

     // 加入一个ApplicationRunner,这时候容器检测到他对Form1有依赖,所以不启动
     container.AddComponent("appRuner",typeof(ApplicationRunner));

     // 加入Form1,此时ApplicationRunner满足依赖需求,Start就开始了
     container.AddComponent("form1",typeof(Form1));
}

 


OK
!这节就到这里,我想大家都清楚一个Facility的概念和用法了吧,有没有感觉到Facility的强大了?用他来@#$%*&组件吧,Bye~(下一节说点什么好呢?各位有建议吗?)

Castle实践3Castle.Services.Transaction

Castle
Project
Project.Pervices.Transaction

   
提供事务的封装和默认的事务管理服务器,其他几个Facility都是以他为基础,比如:AutomaticTransactionManagementNHibernate等。下面让我们从接口着手了解他对事务做了怎样的封装。


IResource:实现了此接口代表一个可以参与事务的资源

publicinterface IResource
{ 
    // 在ITransaction.Begin()ITransaction.Enlist()时调用(准备动作)
void Start();
    // 在ITransaction.Commit()时调用(提交动作)
void Commit();
    // 在ITransaction.Rollback()时调用(回滚动作)
void Rollback();
} 

 


ISynchronization
:用于同步处理

publicinterface ISynchronization
{ 
    // 在ITransaction.Commit()与ITransaction.Rollback()之前调用
void BeforeCompletion();
    // 在ITransaction.Commit()与ITransaction.Rollback()之后调用
void AfterCompletion();
} 

 


接口ITransaction:代表一个事务

publicinterface ITransaction
{ 
    // 准备事务,初始化所属资源
void Begin();
    // 提高事务
void Commit();
    // 回滚事务
void Rollback();
    // 获取当前事务状态
    TransactionStatus Status {get; }
    // 加入一个共享资源
void Enlist(IResource resource);
    // 加入一个同步对象
void RegisterSynchronization(ISynchronization synchronization);
    // 资源上下文环境,可以附加额外的信息
    IDictionary Context { get; }
} 

 

接口ITransactionManager:代表一个事务管理器

publicinterface ITransactionManager 
{ 
    // 创建一个事务 
    ITransaction CreateTransaction( TransactionMode transactionMode, IsolationMode isolationMode ); 
    // 获取当前的事务 
    ITransaction CurrentTransaction 
    {  
        get; 
    } 
    // 销毁特定事务的资源 
void Dispose(ITransaction transaction); 
} 

 



AbstractTransaction实现了ITransaction接口:
    他提供基本的事务资源与事务同步资源的管理和相应的处理逻辑。

StandardTransaction继承于AbstractTransaction类:
    在AbstractTransaction的基础上提供子事务支持。这里为了区别事务的父子关系,就分别将他们称为:父事务和子事务吧。
    一个父事务可以包含多个子事务,一个子事务还可以包含子子事务,在任何一个子事务发生回滚的时候,事务链上的所有事务都不能Commit只是Roolback。

事务模式:

publicenum TransactionMode 
{ 
    Unspecified, 
    Requires, 
    NotSupported, 
    RequiresNew, 
    Supported 
}

 

事务隔离级别:

publicenum IsolationMode
{ 
    Unspecified, 
    Chaos = 0,         
    ReadCommitted,     
    ReadUncommitted, 
    RepeatableRead, 
    Serializable 
}

 


DefaultTransactionManager
实现了ITransactionManager:
    他提供默认的事务管理器,主要负责创建事务和提供当前事务。创建不提供事务隔离级别支持。根据指定的“事务模式”,创建父事务和子事务。不过你完全可以定制自己的事务管理器。
    下面来看看如何使用默认事务管理器:

1)创建一个事务管理器: 
   

DefaultTransactionManager tm = new DefaultTransactionManager(); 

2)从事务管理器中创建一个事务:

    ITransaction transaction = tm.CreateTransaction(TransactionMode.Unspecified, IsolationMode.Unspecified);

如果指定TransactionMode为Unspecified,则事务管理器使用默认的TransactionMode.Requires。这样就创建了一个父事务。
3)父事务创建之后,再以Unspecified或者Requires创建事务,则新创建的事务自动成为父事务的子事务。
4)如果不想创建子事务,则用RequiresNew来创建。

    使用默事务管理器需要注意一下几点:
1)默认的事务管理器都一个堆栈管理,CurrentTransaction返回的始终是最后一个创建的事务。
2)子事务可以有子子事务。
3)创建事务的时候,父事

抱歉!评论已关闭.