分享

Castle实践

 鱼非鱼 2007-03-25

这两天来,把国内国外一些在google搜到的关于ioc,aop相关文档看了一部分,其中我把一些转贴在论坛上了,作为资料收集,希望对大家的学习有帮助。(bbs:http://bbs.,是我的资料收集地)
     
学了东西总得实践,所以我打算写一系列关于这方面应用的文章。net世界里面,目前很火的就是springcastle,我这里先就castle的一些应用心得和大家交流交流,因为我觉得castle的易用性比spring强很多,入门也相对比较容易,当然他们的基本观点都是一致,实现的手法不太相同而已,导致表现出一部分的特性也不尽相同,所以学习的时候,基本的IOC思想,AOP实现原理是必须了解的,板块里有详尽的参考资料,花点时间吃透他吧!
     
当然文中可能有错误的地方,希望大家多多提出批评指教。那么,下面我们就开始吧,从简单到复杂,here we go~~



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./index.php/Facilities
2
关于CastleAOP
AspectSharp Castle提供的AOP轻量级框架,AspectSharp是建立在DynamicProxy的基础上,同时Castle提供了AspectSharp Facilities使你可以更容易的在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./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来形成一个处理流来处理组件模块。

protected virtual void 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 == nullthrow new 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接口的程序在满足依赖性的时候,能够自我执行。

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


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

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


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

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

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

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

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


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

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


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

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

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


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

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

 

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

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

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

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


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

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

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

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

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

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

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

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

    
#region IStartable 成员

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

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

    
#endregion

    
private void StartApp()
    {
        Application.Run( _form );
    }
}


其中Form1是一个普通窗口:

public class Form1 : System.Windows.Forms.Form
{
    
// .
}


最后是Main

[MTAThread]
static void 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:实现了此接口代表一个可以参与事务的资源

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


ISynchronization
:用于同步处理

public interface ISynchronization
{
    
// ITransaction.Commit()ITransaction.Rollback()之前调用
    void BeforeCompletion();
    
// ITransaction.Commit()ITransaction.Rollback()之后调用
    void AfterCompletion();
}


接口ITransaction:代表一个事务

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



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

 

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

 



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

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

事务模式:

public enum TransactionMode 
{
    Unspecified,
    Requires,
    NotSupported,
    RequiresNew,
    Supported
}



事务隔离级别:

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


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

1
)创建一个事务管理器:
    DefaultTransactionManager tm = new DefaultTransactionManager();
2
)从事务管理器中创建一个事务:
    ITransaction transaction = tm.CreateTransaction(TransactionMode.Unspecified, IsolationMode.Unspecified);
如果指定TransactionModeUnspecified,则事务管理器使用默认的TransactionMode.Requires。这样就创建了一个父事务。
3
)父事务创建之后,再以Unspecified或者Requires创建事务,则新创建的事务自动成为父事务的子事务。
4
)如果不想创建子事务,则用RequiresNew来创建。

   
使用默事务管理器需要注意一下几点:
1
)默认的事务管理器都一个堆栈管理,CurrentTransaction返回的始终是最后一个创建的事务。
2
)子事务可以有子子事务。
3
)创建事务的时候,父事务和子事务的顺序要一定。如果创建了一个父事务后,再创建就是一个子事务,再来就是一个子子事务。除非你用了RequiresNew后,就重新往返这个过程。



这次介绍到这里,如果我理解的不正确,请大家多多指点。3q~

Castle实践4Automatic Transaction Management Facility

        在进入第五节的NHibernate Facility之前,先介绍Automatic Transaction Management Facility,它是一个用于自动化管理事务的Facility,根据执行的方法是否抛出异常来决定提交或者回滚事务。
        Automatic Transaction Management Facility
很简单,是辅助性质的Facility。它通过特性来使用的。使用的两个特性都是上一节所介绍的Castle.Services.Transaction中的,分别是:
1
TransactionalAttribute:用于类,表明使用自动事务管理的。
2
TransactionAttribute:用于方法,指定方法使用的事务模式和隔离级别。

以下是使用方法:

WindsorContainer container = new WindsorContainer(store);
container.AddFacility( "auto.transaction", 
new TransactionFacility() );

 

[Transactional]
public class BusinessClass
{
  
public void Load(int id)
  {
    
  }

  [Transaction(TransactionMode.Requires)]
  
public ‘‘‘virtual‘‘‘ void Save(Data data)
  {
    
  }
}


       
这个Facility一般和其他的Facility一起使用。容器会对声明了TransactionAttribute的方法注入一个拦截器(Interceptor),拦截器依赖于一个ITransactionManager
       
它使用ITransactionManager来产生一个事务trans,然后在调用trans.Begin()之后执行方法处理,最后根据方法是否发生异常来trans.Commit()或者trans.Rollback(),最后是调用ITransactionManager.Dispose(trans)来释放资源。这样它就完成了事务自动化过程。
        
需要注意的是:如果你在容器注册使用自动事务管理的组件不是以一个接口来注册的,那么在需要自动事务管理的方法必须声明为:virtual。为什么这样做?你需要了解动态代理的实现机制:http://www./default.aspx/Repository.DynamicProxy

 

Castle实践5NHibernate Facility

        大家好,最近项目正式发布挺忙的,所以这篇东西是拖得挺久的啦。好了,废话不多说,进入正题吧。


NHibernate Facility包括有两个项目:
1
Castle.Facilities.NHibernateExtension
        这里有主要有两个Attribute和一个Session管理器。
        SessionFlushAttribute
:用于方法指定sessionflush方式

[SessionFlush(FlushOption.Auto)]
public virtual Person CreatePerson(string name)
{ }

        UsesAutomaticSessionCreationAttribute:用于类指定使用的session自动管理器使用哪个session factory(配置文件中指定)。

[UsesAutomaticSessionCreation("nhibernate.factory")]
public class PersonDao
{ }

        SessionManager:会话管理器,这是一个PerThread class(LocalDataStoreSlot),用一个堆栈来保存当前会话,在下面介绍的AutomaticSessionInterceptor中会用到这个管理器。

static Stack CurStack
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    
get
    {
        
// _slot是一个内存槽,这个槽是用来装一个Stack的,
        //
这样做用来确保每个线程在_slot只共享一个Stack,他用来
        //
储存Session
        Stack stack = (Stack) Thread.GetData(_slot);
        
if (stack == null)
        {
            stack = 
new Stack();
            Thread.SetData(_slot, stack);
        }
        
return stack;
    }
}



2
Castle.Facilities.NHibernateIntegration
        NHibernate Facility
的核心在这里,这里用到了前面介绍的Castle.Services.Transaction(实践3)和Castle.Facilities.AutomaticTransactionManagement(实践4)。首先,先不说原理,我们来看一下怎么使用。
【使用篇】
第一步:NHibernate Facility配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<facilities>
        
<facility id="nhibernate">
            
<!-- 数据库一 -->
            
<factory id="nhibernate.factory.1">
                
<settings>
                    
<item key="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
                    
<item key="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</item>
                    
<item key="hibernate.connection.connection_string">Server=localhost;Database=Test;Uid=sa;Pwd=123</item>
                    
<item key="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</item>
                
</settings>
                
<resources>
                    
<resource assembly="Demo" name="Demo.Entities.Blog.hbm.xml" />
                    
<resource assembly="Demo" name="Demo.Entities.BlogItem.hbm.xml" />
                
</resources>
            
</factory>
            
<!-- 数据库二 -->
            
<factory id="nhibernate.factory.2">
                
<settings>
                    
<item key="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
                    
<item key="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</item>
                    
<item key="hibernate.connection.connection_string">Server=localhost;Database=Test2;Uid=sa;Pwd=123</item>
                    
<item key="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</item>
                
</settings>
                
<resources>
                    
<resource assembly="Demo" name="Demo.Entities.Order.hbm.xml" />
                
</resources>
            
</factory>
        
</facility>
    
</facilities>
</configuration>

第二步:建立好NHibernate的映射和持久类文件。这里和普通使用nhb没什么不同的地方。
第三步:在DAO中使用上面介绍的UsesAutomaticSessionCreationAttribute

[UsesAutomaticSessionCreation("nhibernate.factory.1")]
public class BlogDao
{
    
public BlogDao() {}

    [SessionFlush(FlushOption.Auto)]
    
public virtual Blog CreateBlog( String name )
    {
        ISession session = SessionManager.CurrentSession;

        Blog blog = 
new Blog();
        blog.Name = name;
        blog.Items = 
new ArrayList();

        session.Save(blog);

        
return blog;
    }
}

第四步:容器初始化

IWindsorContainer container = new WindsorContainer("../../CastleConfig.xml");
container.AddFacility("transaction", 
new TransactionFacility());
container.AddFacility("nhibernate", 
new NHibernateFacility());
container.AddComponent("blog.dao", 
typeof(BlogDao));

第五步:使用

BlogDao dao = (BlogDao)container["blog.dao"];
dao.CreateBlog(
string.Format("test-{0}", new Random().Next()));

使用过程中,CreateBlog会自动根据指定的session factory新建factory,并调用factory生产session来执行任务,最后根据指定的flush模式来flush
如果你要使用自动事务,可以这样:

[Transactional]
[UsesAutomaticSessionCreation("nhibernate.factory.1")]
public class BlogDao
{

    [Transaction(TransactionMode.Requires)]
    [SessionFlush(FlushOption.Auto)]
    
public virtual Blog CreateBlog( String name )
    {
        ISession session = SessionManager.CurrentSession;

        Blog blog = 
new Blog();
        blog.Name = name;
        blog.Items = 
new ArrayList();

        session.Save(blog);

        
return blog;
    }

}

事务管理也是自动的,会自动根据是否发生异常来Commit或者Rollback
这样使用之后,你完全不用写一句OpenSession()或者BeginTrans()又或者是Commit()等等,因为这都被容器自动化管理了。而且可以很方便的使用多个数据库连接,只需要改变一下UsesAutomaticSessionCreationAttribute中的factory id就可以了。Cool吗?


【原理篇】

1
SessionKeeper : Castle.Services.Transaction.ISynchronization
       
事务的同步化对象,这里用于关闭相应的ISession
2
ResourceSessionAdapter : Castle.Services.Transaction.IResource
       
这里Castle.Services.Transaction.ITransaction包装了一个NHibernate.ITransaction作为资源,他是一个对象适配器,把NHibernate.ITransaction适配为Castle.Services.Transaction.IResource,用于NHibernate.ITransaction提交和回滚。
       
上面两个对象都是Castle.Services.Transaction(实践3有详细介绍)里面的,NHibernate Facility用他们协助管理、同步事务。在拦截器AutomaticSessionInterceptor中,如果方法体声明了事务特性,他们将被使用到。
3
SessionFactoryActivator : Castle.MicroKernel.ComponentActivator.AbstractComponentActivator
        NHibernate Facility
在配置ISessionFactory的时候,根据xml配置文件初始化一个NHibernate.Cfg.Configuration,并把这个Configuration作为ISessionFactory组件的一个ExtendedProperties,当请求ISessionFactory的时候,Activator会把ExtendedProperties里面的Configuration取出来,并调用BuildSessionFactory来生产出一个ISessionFactory。这样就可以根据不同的ID(配置时候指定)来请求不同的ISessionFactory了,上面说使用多数据库的时候就是改变一下id,这下明白了吧。当然ISessionFactory的请求是Facility本身做的事,不用你管,要是你想直接用ISessionFactory,那也可以直接向容器索取,同样的你只需要知道一个ID

// 声明一个服务接口为ISessionFactoryComponentModel
ComponentModel model = new ComponentModel(id, typeof(ISessionFactory), null);
// 把配置对象作为组件的扩张属性
model.ExtendedProperties.Add(ConfiguredObject, cfg );
// 组件生存方式为单例
model.LifestyleType = LifestyleType.Singleton;
// 指定Activator
model.CustomComponentActivator = typeof( SessionFactoryActivator );
// 加入到容器
Kernel.AddCustomComponent( model );


4
NHibernateTransactionManager : Castle.Services.Transaction.DefaultTransactionManager
       
这是一个事务管理器,在TransactionInterceptor中用管理器创建Castle.Services.Transaction.ITransaction并加入管理器,在AutomaticSessionInterceptor 中检测到有事务打开,则执行上面提到的12点包装一个NHibernate.ITransaction,并开始事务。
5
TransactionInterceptor  AutomaticSessionInterceptor
       
这两个拦截器是NHibernate Facility的核心,下面详细分析。



我们从注册一个NHibernate Facility开始,看他是如何实现这种自动化管理的。
注册Facility的时候,我们不但向容器注册了NHibernate Facility,而且也注册了Transaction Facility。当使用了TransactionalUsesAutomaticSessionCreation特性,这两个facility会同时发生作用。
NHibernate Facility
执行初始化动作,片断如下:

// 获取session factory配置
IConfiguration factoriesConfig = FacilityConfig.Children["factory"];
if (factoriesConfig == null)
{
    
throw new ConfigurationException(
        "You need to configure at least one factory to use the NHibernateFacility");
}
// 加入一个组件初始化处理流,所有声明了UsesAutomaticSessionCreationAttribute的都植入拦截器
Kernel.ComponentModelBuilder.AddContributor( new AutomaticSessionInspector() );
// 上面植入的拦截器
Kernel.AddComponent( "nhibernate.session.interceptor", typeof(AutomaticSessionInterceptor) );
// 加入事务管理器
Kernel.AddComponent( "nhibernate.transaction.manager", typeof(ITransactionManager), typeof(NHibernateTransactionManager) );
foreach(IConfiguration factoryConfig in FacilityConfig.Children)
{
    
if (!"factory".Equals(factoryConfig.Name))
    {
        
throw new ConfigurationException("Unexpected node " + factoryConfig.Name);
    }
    
// 配置session factory,配置过程请看上面第三点
    ConfigureFactories(factoryConfig);
}


TransactionFacility
会在所有使用了事务特性的方法上面植入拦截器,上面提到的NHibernateTransactionManager 就是在这里发挥作用的。拦截器片断如下:

// 创建一个事务,并自动把事务加入管理器
ITransaction transaction =     _manager.CreateTransaction( transactionAtt.TransactionMode, transactionAtt.IsolationMode );
if (transaction == null)
{
    
return invocation.Proceed(args);
}
object value = null;
// 开始事务
transaction.Begin();
try
{
    
// 执行方法体处理,这里会被AutomaticSessionInterceptor拦截
    value = invocation.Proceed(args);
    
// 正常则提交事务
    transaction.Commit();
}
catch(Exception ex)
{
    
// 发生异常回滚事务
    transaction.Rollback();
    
throw ex;
}
finally
{
    
// 释放事务资源
    _manager.Dispose(transaction); 
}


AutomaticSessionInterceptor
拦截器分析:

// 获取当前的方法使用的数据库的标识id
String key = ObtainSessionFactoryKeyFor(invocation.InvocationTarget);
// 根据标识id判断当前处理是否和session管理器的当前活动数据库相同
if (SessionManager.IsCurrentSessionCompatible(key))
{
    
// 执行处理
    return invocation.Proceed(args);
}
// 获取session factory,上面讲的SessionFactoryActivator会发挥作用
ISessionFactory sessionFactory = ObtainSessionFactoryFor(key);
// 打开一个session
ISession session = sessionFactory.OpenSession();
// sessionkey标识加入管理器
SessionManager.Push(session, key);
// 获取sessionflush模式
FlushOption flushOption = ExtractFlushOption(invocation.MethodInvocationTarget);
ConfigureFlushMode(flushOption, session);
// 当前是否有活动事务,如果有则开始事务
if (EnlistSessionIfHasTransactionActive(key, session))
{
    
try
    {
        
// 执行处理
        return invocation.Proceed(args);
    }
    
finally
    {
        
// 从管理器从移除sesion
        SessionManager.Pop(key);
    }
}
try
{
    
// 不带事务的处理
    return invocation.Proceed(args);
}
finally
{
    
// flush
    if (flushOption == FlushOption.Force)
    {
        session.Flush();
    }
    
// 关闭session
    session.Close();
    
// 移除sesion
    SessionManager.Pop(key);
}


好了,分析就到此为止吧。是不是有点混乱。因为这个Facility和其他好几个Facility结合一起用,关系比较复杂啦。建议大家如果想了解原理的话,还是结合我的分析自己跟踪一下源码的处理吧。



因为是实践性的东西,我希望多以代码的方式剖析给大家看。哪里讲得不对的,希望大家多提宝贵意见,下次实践我们再见咯~~3q~~

Castle实践6TypedFactory Facility

        如何在Castle IOC中使用工厂呢?本节就是你所要的,你会觉得在Castle IOC中使用工厂是件多么容易的事情,并且是高灵活度,低耦合的。

【使用篇】
1
)定义一个产品接口

public interface IProduct
{}


2
)实现一个或者多个产品类

public class ConcreteProduct1 : IProduct
{}

public class ConcreteProduct2 : IProduct
{}


3
定义一个工厂接口

public interface IProductFactory
{
    
// 生产方法
    IProduct Create(String key);
    
// 销毁方法
    void Release(IProduct handler);
}


4
)定义一个配置文件

<configuration>
    
<facilities>
        
<facility id="typedfactory">
            
<factories>
                
<factory id="productFactory" interface="TypedFactoryDemo.IProductFactory, TypedFactoryDemo"
                    creation
="Create" destruction="Release" />
            
</factories>
        
</facility>
    
</facilities>
</configuration>


5
)初始化容器

// 初始化一个容器
IWindsorContainer container = new WindsorContainer("typedFactory_config.xml" );
// 加入Facility
container.AddFacility( "typedfactory", new TypedFactoryFacility() );
// 加入产品
container.AddComponent( "prod1", typeof(IProduct), typeof(ConcreteProduct1) );
container.AddComponent( "prod2", 
typeof(IProduct), typeof(ConcreteProduct2) );


6
)使用工厂

// 从容器中获取工厂
IProductFactory factory = (IProductFactory)container["productFacotry"];
// 进行生产
IProduct prod1 = factory.Create("prod1");  // prod1 is ConcreteProduct1
IProduct prod2 = factory.Create("prod2"); 
// prod2 is ConcreteProduct2



        OK
你不需要写任何的具体的工厂方法,也不用new任何一个具体的产品。增加一个工厂或者是增加一个产品,是不是很简单呢?想知道其中的奥秘吧,那请你继续往下看。

【原理篇】
        
这个Facility中有一个叫FactoryEntry的类,专门是用来收集初始化的工厂信息的。包括工厂ID、工厂接口、工厂的创建产品方法和工厂的销毁产品方法。
       
而在TypedFactoryFacility中,容器首先对配置的工厂初始化一个FactoryEntry工厂信息库放到工厂ModelExtendedProperties中,然后容器会加入一个工厂接口的拦截器FactoryInterceptor,当从容器中获取一个工厂的时候,就会被拦截器拦截。
       
在拦截器的处理里面会自动根据工厂的创建方法名称(FactoryEntry.CreationMethod)来向容器中索取一个产品对象。又或者是销毁方法名称(FactoryEntry.DestructionMethod)在容器中ReleaseComponent一个产品对象。

除此之外,你还可以这样定义工厂接口:

public interface IProductFactory
{
    
// 生产方法
    ConcreteProduct Create();
}


如果你想要自己的工厂生产方法,那也完全没问题,只要你实现自己的工厂方法而方法的名称和配置文件中的creation或者destruction指定的方法名称不同就可以了。

【后记】
此外,在看源码过程中,有两处是值得探讨的。由于前项目阶段性完成,今天要回深圳,所以我就贴出来让大家看下,一起研究研究。xixi :)

// 是这样得到工厂类型的
ITypeConverter converter = (ITypeConverter)_kernel.GetSubSystem( SubSystemConstants.ConversionManagerKey );
Type factoryType = (Type)converter.PerformConversion( config.Attributes["interface"], 
typeof(Type) );



// Empty 是个空类哦~
ComponentModel model = new ComponentModel(entry.Id, entry.FactoryInterface, typeof(Empty));


see you then~

 

Castle实践7BatchRegistration Facility

Posted on 2005-07-13 15:00 绿叶 阅读(1539) 评论(8)  编辑 收藏 收藏至365Key

        BatchRegistration Facility是一个辅助Castle IOC容器配置的扩张单元。我们使用他可以很方便在容器中加载ComponentFacility,你可以把单独的Component或者Facility编译成dll,然后通过配置文件用BatchRegistration Facility“一次性或者筛选后加入到容器中而无需硬编码AddComponentAddFacility,灵活性是很高的~
 
1
)加载程序集中所有定义了Castle.Model.CastleComponentAttribute 的组件

<facility id="batchregistration">
    <assemblyBatch name="MyAssembly" useAttributes="true" />
</facility>



2
)除Component1不会加载,其他定义了CastleComponentAttribute都会被加载

<facility id="batchregistration">
    <
assemblyBatch name="MyAssembly" useAttributes="true">
       
<exclude type="MyAssembly.Component1" />
    </assemblyBatch>
</
facility>


3
)只加载Component2

<facility id="batchregistration">
    
<assemblyBatch name="MyAssembly" useAttributes="false">
        
<include key="other" component="MyAssembly.Component2" />
    
</assemblyBatch>
</facility>



4
)加载Faclity

<facility id="batchregistration">
    
<addFacility id="facility1" type="MyAssembly.Facility1, MyAssembly" />
    
<addFacility id="facility2" type="MyAssembly.Facility2, MyAssembly" />
</facility>


       
其实这个Facility做的就是根据配置扫描程序集然后动态加载到容器里面,虽然很简单却很灵活。比如你想更改一个component的行为,你只需要替换掉这个componentdll就行了,其他部分无需重新编译。

【使用方法】
1
)建立这个Facility的配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<facilities>
        
<facility id="batchregistration">
            
<assemblyBatch name="MyAssembly" useAttributes="true" />
        
</facility>
    
</facilities>
</configuration>



2
)声明组件的class并把他编辑成配置文件里面MyAssembly.dll.

[CastleComponent("comp1")]
public class Component1
{
    
public Component1()
    {
    }
}

[CastleComponent("comp2")]
public class Component2
{
    
public Component2()
    {
    }
}



3
)使用Facility,这样Component1Component2就自动被加载了。

IWindsorContainer container = new WindsorContainer("../../CastleConfig.xml"); 
container.AddFacility( "batchregistration", 
new BatchRegistrationFacility() );

 

Castle实践8AspectSharp

        由于我最近参加雅思培训,一天到晚都在上课,所以一直没时间出来写blog。今天星期六我把aop资料整理了,做了例子,现在放上来,希望堆大家有帮助。
        AOP
是基于动态代理技术的。在学习AOP之前必须明白几个概念,这几个概念我将由配置AspectSharp(以下简称A#)的配置文件中一一引出,包括:AdvicePointcutAdvisterMixin 
        A#
有自己独特的配置语言,当然也支持XML,但是新的配置语言我觉得比XML更加明了直观,而且也是非常容易使用的。 详细的官方文档在这里:http://www./index.php/AspectSharp_Language_Documentation

1
)配置必须按照以下顺序:

[Imports]

[Global Interceptor map]

[Global Mixin map]

Aspects definitions


       
2
[Imports]:引入命名空间,在下面的配置中用到的拦截器、混淆器所需要的。

Import Namespace.Name [in AssemblyName]



3
[Global Interceptor map]:如果你想在程序共享同一个拦截器而不想重复声明可以将Interceptor声明为全局,在同一配置文件中重用,而不用再次打长长的名称,用声明的别名就可以了。

interceptors [ 
  "key" : InterceptorType ; 
  "key2" : InterceptorType2 
]



4
[Global Mixin map] 同样混淆器也可以声明为全局。

mixins [ 
  "key" : MixinType ; 
  "key2" : MixinType2 
]



5
Aspects definitions 具体定义一个切面(需要拦截的地方)

aspect Name for Type 
  [include]
  [pointcuts]
end



6
[include] :定义混淆器(mixin)组合的类

aspect MyAspect for Customer
  include DigitalGravity.Mixins.Security in DigitalGravity.XProject
  include System.Collections.ArrayList in System
end



7
[pointcuts] :拦截的具体名称,这里先指定拦截的类型并可以用通配符匹配名称。类型如下:

·  method :拦截方法的名称

·  property :拦截的属性名称

·  propertyread :拦截的读属性的名称

·  propertywrite:拦截的写属性的名称

pointcut method|property(*)
end

pointcut method|propertyread(*)
end

pointcut propertywrite(*)
end


8
Advices :指定由哪个拦截器拦截

aspect MyAspect for Customer
  pointcut method(*)
    advice(DigitalGravity.Interceptors.LogInvocationInterceptor in DigitalGravity.XProject)
  
end
end

        上面基本是官方的配置例子,那么这几个咚咚有什么关系,引用一句话“advice是你想向别的程序内部不同的地方注入的代码。pointcut定义了需要注入advice的位置,通常是某个特定的类的一个public方法。advisorpointcutadvice的装配器,是将advice注入主程序中预定义位置的代码。这句话是从java关于spring文章中截取出来的,原文在这里:http://www./bbs/topic.aspx?topicid=209&page=65535,上面所讲的advicepointcut的概念和这里是一样的,对于advisor我想是相对于上面讲的Aspects definitions 。这样大家就非常容易理解了吧。

详细了解动态代理请看:http://iaxes.cnblogs.com/archive/2005/04/07/132868.html
详细了解Maxin请看:http://iaxes.cnblogs.com/archive/2005/04/07/133407.html
AOP
基本资料请看:http://www./bbs/topic.aspx?topicid=203


        AOP-面向方面编程,定义这里我不想介绍太多,如果有兴趣的话,你可以来:http://www./bbs/board.aspx?boardid=14看看aop的资料。那么AOP主要应用在哪些方面呢?具体来说,有:
Authentication
--权限、Caching--缓存、Context passing--内容传递、Error handling--错误处理、Lazy loading--延迟加载、Debugging--调试、logging tracing profiling and monitoring--记录跟踪 优化 校准、Performance optimization--性能优化、Persistence--持久化、Resource pooling--资源池、Synchronization--同步、Transactions--事务……
        A#
CastleProject.Net上实现AOP的一个框架,另外Spring.Net也有AOP的咚咚。 其实在Java里面,aop已经是很久以前的话题了,只是。net方面资料不如java多,用的人也相对少了。我下面将以一个简单的例子(参照官方改写),阐述A#.net下面实现简单的日志权限检查AOP编程。


1
)配置文件:

import AopDemo.Interceptors
import AopDemo.Mixins

aspect 
log for [AopDemo]
    pointcut method(*)
        advice(LoggerInterceptor)
    
end
end

aspect Security 
for [AopDemo]
    include SecurityMixin
    pointcut method(*)
        advice(SecurityCheckInterceptor)
    
end
end

最上面两个import是引入命名空间,是LoggerInterceptorSecurityCheckInterceptorSecurityMixin的。我发现如果在同一个程序集中,不使用import也行,但是如果你的interceptor是单独的dll的话,应该必须import,我没有尝试。把interceptor独立出来的好处就是可以很方便的替换他。
接着是两个切面定义,第一个是定义所有的方法都会被LoggerInterceptor拦截,用于记录日志。上面讲到可以利用通配符来筛选拦截的方法名称,所以“*”就是代表所有的方法,又比如说:pointcut void Create(*)就是拦截返回类型为void,参数不限制的Create方法,void create(int id), void create(string name)这两个方法都会被拦截。第二个pointcut是加入了一个权限混淆器SecurityMixin,他的所有方法调用会被SecurityCheckInterceptor拦截。SecurityMixin的代码如下:

public class SecurityMixin : ISecurity, IProxyAware
{
    
private object proxy;

    
public SecurityMixin()
    {}

    
#region ISecurity 成员

    
public bool Access
    {
        
get
        {
            
// use proxy to do sth.
            // eg: if (proxy is PersonDao)

            
return true;
        }
    }

    
#endregion

    
#region IProxyAware 成员

    
public void SetProxy(object proxy)
    {
        
this.proxy = proxy;
    }

    
#endregion
}

他实现了两个接口:ISecurityIProxyAware
ISecurity
有一个Access属性,而实现IProxyAware是为了在mixin中使用代理对象。这样定义之后,在SecurityCheckInterceptor拦截的代理对象就是实现了ISecurity接口的对象,可以调用ISecurity接口中定义的方法了,而原来的对象是没有实现这个接口的。

使用的时候先加载配置初始化AspectEngine

AspectLanguageEngineBuilder builder = new AspectLanguageEngineBuilder(File.OpenText(@"../../aspectsharp.cfg"));
AspectEngine engine = builder.Build();

然后通过这样来使用:

PersonDao dao = engine.WrapClass(typeof(PersonDao)) as PersonDao;
dao.Create("CCC");

Create方法将被拦截咯,作日志,安全检查,随便你~

好了,就说到这吧,下载例子来调试调试咯:
http://www./Income/Others/Castle.AopDemo.rar
注意例子中的PersonStorePersonDao是模拟数据库的操作的,方便大家调试。bye

Castle实践9-在Castle IOC容器中使用AspectSharp(全面剖析AspectSharp Facility)

        “Castle实践 8”介绍了A#的使用方法,那么A#如何和Castle IOC容器结合起来使用呢?在Castle的官方falicicies库中,提供了AspectSharp Facility来让我们可以很简单在IOC中使用A#。我把使用方法放在最后,题目说了是要全面剖析这个facility的原理,借助这次分析让我们对Castle IOC中的Falicity编写有更深的了解。

       
编写一个facility之前,最重要的就是要明确这个falicity的目的。通过实践 8”知道,要使用一个aop过的对象,需要调用engine.WrapClass或者engine.WrapInterface来对目的对象包装,那么得出这个facility的目的就是:当用户向IOC容器请求组件的时候,根据aop的配置自动包装组件再交给客户使用。

       
明白了需求,那么就开始分析吧:

protected override void Init()
{
    
if (FacilityConfig == nullreturn;

    
// 第一步
    RegisterAspectEngine();
    
// 第二步
    RegisterInterceptor();

    
// 第三步
    Kernel.ProxyFactory = new AopProxyFactory();

    
// 第四步
    _engine = (AspectEngine) Kernel[ typeof(AspectEngine) ];

    
// 第五步:向IOC里面加入任何组件的时候,OnComponentRegistered会回调
    Kernel.ComponentRegistered += new ComponentDataDelegate(OnComponentRegistered);
}



第一步:

private void RegisterAspectEngine()
{
    
// 获取当前facility的配置,相当于获取了a#的配置
    String contents = FacilityConfig.Value;

    
// 创建a#builder
    AspectEngineBuilder builder = new AspectLanguageEngineBuilder(contents);

    ComponentModel model = 
        
new ComponentModel("aspectsharp.engine", 
            
typeof(AspectEngine), typeof(AspectEngine));
    
    
// a#builder作为扩张属性
    model.ExtendedProperties.Add("builder", builder);
    
// engine激活的时候执行AspectEngineActivator
    model.CustomComponentActivator = typeof(AspectEngineActivator);

    
// a# engine作为组件加入ioc
    Kernel.AddCustomComponent( model );
}



第二步:

private void RegisterInterceptor()
{
    
// AopInterceptor加入IOC
    Kernel.AddComponent( "aspectsharp.interceptor", typeof(AopInterceptor) );
}



第三步:注册iocproxy工厂

/// <summary>
/// Specialization of <see cref="Castle.Windsor.Proxy.DefaultProxyFactory"/>
/// that checks for aspects in the model and potential mixins.
/// </summary>
public class AopProxyFactory : DefaultProxyFactory
{
protected override void CustomizeContext(GeneratorContext context, IKernel kernel, 
    ComponentModel model, 
object[] arguments)
{
    
// 获取a#的配置
    AspectDefinition aspect = (AspectDefinition) model.ExtendedProperties["aop.aspect"];

    
if (aspect == nullreturn;

    
// 得到所有mixin
    MixinDefinitionCollection mixins = aspect.Mixins;

    
foreach(MixinDefinition definition in mixins)
    {
        Type mixinType = definition.TypeReference.ResolvedType;
        
        
try
        {
            
// 创建一个minix对象并交给dynamicproxydynamicproxy产生proxy的时候会用到
            context.AddMixinInstance( Activator.CreateInstance( mixinType ) );
        }
        
catch(Exception e)
        {
            
throw new ApplicationException("Could not instantiate mixin " + mixinType.FullName, e);
        }
    }
}

protected override void CustomizeProxy(object proxy, GeneratorContext context, IKernel kernel, ComponentModel model)
{
    
// 获取在上面的CustomizeContext函数中创建的mixin对象
    object[] mixins = context.MixinsAsArray();

    
// 所有实现了IProxyAwaremixin对象,都会把实际的代理set到里面交给客户处理
    //
“Castle实践 8”中的SecurityMixin就是实现了IProxyAware的,在SecurityMixin
    //
上下文中可以任意使用proxy,这下明白了吧。
    for(int i=0; i < mixins.Length; i++)
    {
        
object mixin = mixins[i];
        
        
if (mixin is IProxyAware)
        {
            (mixin 
as IProxyAware).SetProxy(proxy);
        }
    }
}



第四步:激活a# engine,导致AspectEngineActivator执行

protected override object InternalCreate()
{
    
// 获取a#builder
    AspectEngineBuilder builder = (AspectEngineBuilder) 
        
base.Model.ExtendedProperties["builder"];

    System.Diagnostics.Debug.Assert( builder != 
null );

    
// 创建engine
    return builder.Build();
}



第五步:向IOC里面加入任何组件的时候,OnComponentRegistered会回调

private void OnComponentRegistered(String key, IHandler handler)
{
    
// 检查a#配置中是否包含组件的切面
    //
就是当前加入的这个组件是否需要aop(怎么说呢??表达不太清晰,大家应该明白吧。请原谅~~
    AspectDefinition[] aspects = 
        _engine.AspectMatcher.Match( handler.ComponentModel.Implementation, 
        _engine.Configuration.Aspects );

    
if (aspects.Length != 0)
    {
        
// 如果组件需要aop,则合并配置中对此组件的切面定义
        //
并将定义加入到组件的扩张属性中
        handler.ComponentModel.ExtendedProperties["aop.aspect"] = Union(aspects);

        
// 向组件加入拦截器
        //
当向ioc请求组件对象的时候,拦截器的作用就是根据上面的定义来产生一个组件的proxy
        handler.ComponentModel.Interceptors.Add( 
            
new InterceptorReference( typeof(AopInterceptor) ) );
    }
}

private AspectDefinition Union(AspectDefinition[] aspects)
{
    
// 这里作用是合并对组件的切面配置内容
    //
但作者todo了,未完成?

    
if (aspects.Length == 1)
    {
        
return aspects[0];
    }

    
// TODO: Merge aspects

    
return aspects[0];
}

 


        
这时候回头来看看,我们的目的是自动包装,上面的代码中没有调用A# engineWrapClassWrapInterface的,其实两个wrap做的是调用DefaultProxyFactory来产生代理的,这里给AopProxyFactory代替了。而真正请求一个组件的时候,产生代理的工作都是在AopInterceptor中处理的。

/// <summary>
/// Summary description for AopInterceptor.
/// </summary>
[Transient]
public class AopInterceptor : IMethodInterceptor, IOnBehalfAware
{
    
private IKernel _kernel;
    
private AspectEngine _engine;
    
private IInvocationDispatcher _dispatcher;

    
public AopInterceptor(AspectEngine engine, IKernel kernel)
    {
        _engine = engine;
        _kernel = kernel;
    }

    
public void SetInterceptedComponentModel(ComponentModel target)
    {
        
// 获取组件的aop配置
        AspectDefinition aspectDef = (AspectDefinition) 
            target.ExtendedProperties["aop.aspect"];

        System.Diagnostics.Debug.Assert( aspectDef != 
null );

        
// InvocationDispatcher用于proxy的方法分派,ContainerInvocationDispatcher重写了
        //ObtainInterceptorInstance
方法,唯一的作用是:尝试从IOC中获取拦截器对象
        _dispatcher = new ContainerInvocationDispatcher(aspectDef, _kernel);
        _dispatcher.Init(_engine);
    }

    
public object Intercept(IMethodInvocation invocation, params object[] args)
    {
        
// a#内幕:_dispatcher.Intercept会处理客户的函数调用
        //Intercept
方法会从ObtainInterceptorInstance获取拦截器实例
        //
ContainerInvocationDispatcher中重写的方法起作用了)
        // 
如果没有拦截器则直接Proceed,有拦截器则在拦截器进行处理
        //
这是a#拦截的一个简单过程,详细你可以参考:a#中的DefaultInvocationDispatcher源码
        return _dispatcher.Intercept( (IInvocation) invocation, args);
    }
}



而在ContainerInvocationDispatcher重写的ObtainInterceptorInstance是这样的:

/// <summary>
/// 获取拦截器对象
/// </summary>
protected override IMethodInterceptor ObtainInterceptorInstance(Type adviceType)
{
    
if (_kernel.HasComponent( adviceType ))
    {
        
// 方式一:从IOC中获取
        //
如果我们把拦截器注册进IOC里面,这里就直接获取
        try
        {
            
return (IMethodInterceptor) _kernel[adviceType];
        }
        
catch(InvalidCastException ex)
        {
            
// In this case, the specified interceptor
            // does not implement the IMethodInterceptor from
            // AopAlliance

            String message = String.Format("The interceptor {0} does " + 
                "not implement AopAlliance.Interceptor.IMethodInterceptor", adviceType.FullName); 

            
throw new ApplicationException(message, ex);
        }
    }

    
// 方式二:从A#DefaultInvocationDispatcher中获取
    //A#
中对拦截器对象实例是有缓存处理的(一个HashTable_type2AdviceInstance
    return base.ObtainInterceptorInstance(adviceType);
}



       
分析结束啦~ ,最后放上使用方法,很简单的:

1
)配置:

<configuration>
    
<facilities>
        
<facility id="aspectsharp">
<![CDATA[
import AopDemo.Interceptors
import AopDemo.Mixins

aspect log for [AopDemo]
    pointcut method(*)
        advice(LoggerInterceptor)
    end
end

aspect Security for [AopDemo]
    include SecurityMixin
    pointcut method(*)
        advice(SecurityCheckInterceptor)
    end
end
]]>
        
</facility>
    
</facilities>
</configuration>



2
)初始化容器:

container = new WindsorContainer(@"../../aspectsharp.ioc.xml");
container.AddFacility("aspectsharp", 
new AspectSharpFacility()) ;
container.AddComponent("persondao", 
typeof(PersonDao));



3
)使用组件:

PersonDao dao = container["persondao"] as PersonDao;
dao.Create("AAA");
...


完整demo下载地址:
http://www./Income/Others/Castle.AspectSharp%20Facility.Demo.rar

bye~

补充:由于我下载的源码里面 Union 函数是TODO,所以你在demo中可以发现,第二个point cut不起作用,原因就是这里。你可以把配置文件修改成这样:

import AopDemo.Interceptors
import AopDemo.Mixins

aspect Security for PersonDao
include SecurityMixin
pointcut method(*)
advice(SecurityCheckInterceptor)
advice(LoggerInterceptor)
end
end

希望castle project那边能够更新得快点吧~也希望能更多人来关注castle

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多