MEF是微软的一个ioc框架,使用非常方便,我们只需要在需要导出的类上标记[Export],在需要使用的地方[import]就可以使用了。现在我们扩展MEF,在其装配生成实例时,使用Castle DynamicProxy对其生成透明代理。
我们先实现一个拦截器,并且继承Attribute基类,这个我们可以将这个拦截器作为类的特性来使用。
using Castle.DynamicProxy; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MefAopTest { public class LogInterceptor : Attribute,IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("begin"); invocation.Proceed(); Console.WriteLine("end"); } } }
这个拦截器中,我们对拦截的方法执行前和执行后都增加了操作。
然后我们新增一个类,这个类使我们需要导出的类。
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Text; namespace MefAopTest { [Export(typeof(ClassA))] [LogInterceptor] public class ClassA { public virtual void GetA() { Console.WriteLine("GetA"); } } }
在这个类上,我们增加特性 [Export(typeof(ClassA))],这是MEF的基本用法。[LogInterceptor]是我们定义的拦截器,我们可以拦截GetA方法。需要拦截的方法必须是虚方法。
下面我们来扩展下MEF。
ExportProvider 类:检索与指定的 ImportDefinition 对象相匹配的导出。这个类的作用很明确,就是用来寻找与 ImportDefinition 对象匹配的ExportDefinition。
我们新增一个基础自ExportProvider 的类。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.Primitives; using Castle.DynamicProxy; namespace MefAopTest { public class AOPExportProvider : ExportProvider, IDisposable { private CatalogExportProvider _exportProvider; public AOPExportProvider(Func<ComposablePartCatalog> catalogResolver) { _exportProvider = new CatalogExportProvider(catalogResolver()); } public ExportProvider SourceProvider { get { return _exportProvider.SourceProvider; } set { _exportProvider.SourceProvider = value; } } protected override IEnumerable<Export> GetExportsCore( ImportDefinition definition, AtomicComposition atomicComposition) { IEnumerable<Export> exports = _exportProvider.GetExports(definition, atomicComposition); return exports.Select(export => new Export(export.Definition, () => GetValue(export))); } private object GetValue(Export innerExport) { object value = innerExport.Value; Type t = value.GetType(); IInterceptor[] attribs = t.GetCustomAttributes(typeof(IInterceptor), true).Cast<IInterceptor>().ToArray(); ProxyGenerator generator = new ProxyGenerator(); object proxy = generator.CreateClassProxy(value.GetType(), attribs); return proxy; } public void Dispose() { _exportProvider.Dispose(); } } }
在装配时,会触发GetExportsCore方法,去寻找相对应的导出。我们在GetValue方法中,使用Castle生成动态代理。
IInterceptor[] attribs = t.GetCustomAttributes(typeof(IInterceptor), true).Cast<IInterceptor>().ToArray();
上面这行代码主要是从需要导出的类的自定义特性中,寻找拦截器。我们这里定义的是[LogInterceptor]。
然后我们再生成透明的动态代理
ProxyGenerator generator = new ProxyGenerator(); object proxy = generator.CreateClassProxy(value.GetType(), attribs);
下面我们新增一个窗体类,窗体上有一个按钮,点击按钮,触发ClassA实例的GetA方法。看是否可以进行拦截。
namespace MefAopTest { public : Form { public Form1() { InitializeComponent(); ComposeParts(); } [Import(typeof(ClassA))] private ClassA c1; public void ComposeParts() { Func<ComposablePartCatalog> catalogResolver = () => { var catalog = new AggregateCatalog(); AssemblyCatalog assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); catalog.Catalogs.Add(assemblyCatalog); return catalog; }; AOPExportProvider provider = new AOPExportProvider(catalogResolver); CompositionContainer container = new CompositionContainer(provider); provider.SourceProvider = container; container.ComposeParts(this); } private void Test_Click(object sender, EventArgs e) { c1.GetA(); } } }
在代理里,我们定义了一个字段,用来Import ClassA类。
[Import(typeof(ClassA))] private ClassA c1;
在我们装配代码中,使用我们扩展的AopExportProvider
我们测试下,点击按钮,看看输出的情况。
好的,我们成功的拦截了ClassA的GetA方法,并在方法执行前和执行后都增加了操作。
这种AOP的实现,我们只需要在需要导出的类上增加一个特性就行了。如果有其他需求,我们可以再增加拦截器,并在相应的类上增加特性就行了,很方便。美中不足的是,需要按照MEF的方式去生成实例,不能像postsharp那样。