.NET 4 实践 - 使用dynamic和MEF实现轻量级的AOP组件 (4)

借花献佛

前面我们介绍了构成DynamicAspect绝大部分的类,为了让Aspect能够自动实施到目标对象,需要提供一种动态装载Aspect机制,在设计DynamicAspect的最初版本,笔者使用配置文件,通过在配置文件提供一个自定义的DynamicAspect节,然后在那里用XML元素来描述每个Aspect的类型以及要编织的目标类型,程序在运行过程中,通过配置文件获取Aspect的信息,然后将相应的Aspect装入我们设计的对象链。配置文件有着其它方法所没有的优势,那就是真正的做到动态的编织,也就是说当需求发生变化的时候,只要修改配置文件,不需要重新编译主程序或者原有的源代码文件,甚至不用暂停正在运行的程序。但是采用配置文件也有它的缺点,由于配置文件采用XML,不便检查配置文件中的值是否正确,错误倾向性比较大,而且要求应用程序开发人员和维护人员都必须熟悉DynamicAspect。经过仔细的评估,后决定改用MEF来实现自动查找和自动匹配功能,而且MEF内建的对象生命期管理机制还可以简化DynamicAspect的架构,下面我们也简要的介绍一下MEF(详细的介绍见MEF官方文档):

MEF的全称是 Managed Extensibility Framework(托管的可扩展框架),最初看到它的时候,那还是在两年前MSDN站点的Code Gallery,一年前它移居Codeplex,一不留神它成了.NET类库的永久公民。据说是MS内部各个开发团队在开发各种的项目中各自为政,每个团队都有自己一套扩展框架,这些扩展框架都特定于各自的应用,很难被其他团队重用,于是就有了MEF出面来一统混乱的天下,这也算是一种自然吧。现如今各团队是不是正忙着把整合MEF呢?

不知道有多少人知道MAF(Managed Add-in Framework),MAF.NET 3.5的时候被加入到类库中,如果你在项目中添加System.AddIn.dll的引用,你就可以看到System.AddIn的名称空间,在VS2008的MSDN文档中你可以找到它的介绍以及使用MAF的例子。MAFMEF都是为支持开发可扩展应用程序而被加入到类库的,MAF着重处理应用程序的隔离和组件的装载和卸载,而MEF则更多的关注可发现、可扩展和可移植方面。MEF的工作原理可以总结成三个词:Export (导出)、Import (导入)和Composite(组合)。在MEF中,参与导入和导出的对象成为Part(部件),在应用中,MEF需要一个宿主(Host)来存放它的根容器,为了能够发现导出和导入的部件,MEF设计了几个不同的Catalog类来指定要查找的部件的范围,比如说TypeCatalog类指定要查找的部件的类型,而AssemblyCatalog则表示要查找的部件所在的程序集,DirectoryCatalog指定要查找的部件所在的文件目录,这些Catalog可以通过一个AggregateCatalog类组合在一起。除了MEF可以通过catalog来自动发现部件之外,在具体的应用中,也可以显式的调用容器的CompositePart扩展方法把部件加入到容器中。让我们结合代码来看看DynamicAspect是如何借用MEF这朵花的。

首先是宿主的选择,从目前能够找到的MEF例子,MEF的宿主都是放在主运行程序这一边,考虑到主程序未必是一个采用了MEF应用,所以决定在DynamicAspect中创建一个自己的容器对象,该容器既可以作为根容器,也可以看做是一个子容器被嵌入到一个父容器中(如果有的话),在DynamicAspect中,要处理部件的类型就是IAspect接口,无论是导入还是导出。实例化一个容器部件的过程是在WeaverExtensiong这个静态类中进行的:

   public static class WeaverExtension
   {

        public static CompositionContainer Container = null;

        static WeaverExtension()
        {
             AggregateCatalog catalog =new AggregateCatalog(
                                        new DirectoryCatalog(@".\"),
                                        new DirectoryCatalog(@".\", "*.exe"));

             Container = new CompositionContainer(catalog);
        }
 
   }
 
这是借用了静态类的特性,一个静态类在程序集被装入是就会被初始化,且静态成员是全局唯一的。在这里,我们定义了一个MEF的CompositionContainer容器对象,该对象在程序集初始化的时候被实例化,我们在容器中指定MEF搜索程序集所在目录的全部dll文件和exe文件,DirectoryCatalog的构造器第二个参数用来指定要匹配的文件,缺省情况下是*.dll。有朋友问了,MEF如何知道要查找的部件是IAspect类型呢?好问题,其实MEF不知道,它只是很忠实地查找所有施加了ExportAttribute或者ImportAttribute的类,并按照指定的匹配规则来进行匹配,这里的匹配不是指DyanmaicAspect中的Aspect匹配目标类型,而是指一个Export和一个Import部件的匹配。
让我们回头看一下AspectBase类的定义:
   [InheritedExport(typeof(IAspect))]
    public abstract class AspectBase : IAspect
   { … }
这里施加的特性是InheritedExport,如果是ExportAttribute的话,由于ExportAttribute被设计成不能施加到派生类,所有的派生类都需要声明ExportAttribute,MEF的设计者充分考虑到这一点,为我们准备了InheritedExportAttribute,顾名思义InheritedExport就是可以沿着继承链传递所施加的特性。
我们的Aspect就这样被导出了。导入在哪里?不要着急,导入在WeavableObject类中定义:
[ImportMany(typeof(IAspect), 
                    AllowRecomposition = true, 
                    RequiredCreationPolicy= CreationPolicy.Any)]
public IEnumerable<IAspect> Aspects { get; set; }
 
看到了,这里也不是ImportAttribute,而是ImportManyAttribute,这是因为Import只是导入一个对象,而我们的IAspect可能有多个,所以就要用到ImportMany了,特性中的两个参数都是自解释的,AllowRecomposition表示在有新的类型导入时,重新对容器中的部件进行匹配。为了每次新的Aspect对象被导入的时候,及时装配到AspectChain,我们为WeavableObject实现一个IPartImportsSatisfiedNotification,该接口只有一个方法:
public void OnImportsSatisfied()
{
    var query = Aspects
                .Where(a => a.IsMatch(typeof(T).FullName) && a.Enabled == true)
                .OrderBy(a => a.Sequence);
          
    foreach (var asp in query)
             AddAspect(asp);
}
 
OnImportsSatisfied在导入满足一次匹配的时候被触发,方法代码使用LINQ查询过滤掉不能和当前目标相匹配或者Enabled等于false的Aspect。
AddAspect的方法如下:
public void AddAspect(IAspect aspect)
{
    if (aspect != null)
    {
        lock (new object())
        {
            if (chain.FirstOrDefault(c =>
                  aspect.GetType().IsAssignableFrom(c.GetType())) == null)
               chain.AddLast(aspect);
        }
    }
}
一个LINQ语句用来防止相同对象的重复装配。上锁是为了线程安全(该语句在新的更新中已经各改为:lock(((ICollection)chain).AynxRoot) )
DynamicAspect的故事到这里,似乎可以告一段落了,但故事并没有结束。DynamicAspect还是一棵幼苗,需要在大家的关怀和呵护中成长。同时,笔者也鼓励朋友们试着把DynamicAspect用在不同的场合,如果有什么问题和建议的话可以在这里回帖或者通过电子邮件反馈给我,先行谢过了。

最后要说明的是,DynamicAspect还在开发之中,还需要经受更多的考验来证明它的价值,笔者会在适当的时候续写DynamicAspect的成长历程,希望届时有更多的精彩奉献个大家。

(全文完)

 
posted @ 2010-07-23 15:12  NiceWk  阅读(5736)  评论(12编辑  收藏  举报