多年之前利用IL Emit写了一个名为Dora.Interception(github地址,觉得不错不妨给一颗星)的AOP框架。前几天利用Roslyn的Source Generator对自己为公司写的一个GraphQL框架进行改造,性能得到显著的提高,觉得类似的机制同样可以用在AOP框架上,实验证明这样的实现方式不仅仅极大地改善性能(包括执行耗时和GC内存分配),而且让很多的功能特性变得简单了很多。这并不是说IL Emit性能不好(其实恰好相反),而是因为这样的实现太复杂,面向IL编程比写汇编差不多。由于AOP拦截机制涉及的场景很多(比如异步等待、泛型类型和泛型方法、按地址传递参数等等),希望完全利用IL Emit高效地实现所有的功能特性确实很难,但是从C#代码的层面去考虑就简单多了。(拙著《ASP.NET Core 6框架揭秘》6折优惠,首印送签名专属书签)
[1]编程体验
除了性能的提升和保持低侵入性,Dora.Interception在编程方式上于其他所有的AOP框架都不太相同。在拦截器的定义上,我们并没有提供接口和基类来约束拦截方法的实现,而是采用“基于约定”的编程模式将拦截器定义成一个普通的类,拦截方法上可以任意注入依赖的对象。在如何应用定义的拦截器方面,我们提供了常见的“特性标注”的编程方式将拦截器与目标类型、方法和属性建立关联,我们还提供了一种基于“表达式”的拦截器应用方式。Dora.Interception主张将拦截器“精准”地应用到具体的目标方法上,所以提供的这两种方式针对拦截器的应用都是很“明确的”。如果希望更加灵活的拦截器应用方式,通过提供的扩展可以自由发挥。本章通过一个简单实例来演示一下Dora.Interception如何使用。阅读更多…
[2]基于约定的拦截器定义方式
Dora.Interception有别于其他AOP框架的最大的一个特点就是采用针对“约定”的拦截器定义方式。如果我们为拦截器定义了一个接口或者基类,那么拦截方法将失去任意注册依赖服务的灵活性。除此之外,由于我们采用了动态代码生成的机制,我们可以针对每一个目标方法生成对应的方法调用上下文,所以定义在拦截上下文上针对参数和返回值的提取和设置都是泛型方法,这样可以避免无谓的装箱和拆箱操作,进而将引入拦截带来的性能影响降到最低。阅读更多…
[3]基于特性标注的拦截器注册方式
在Dora.Interception中按照约定方式定义的拦截器可以采用多种方式注册到目标方法上。本篇文章介绍最常用的基于“特性标注”的拦截器注册方式,下一篇会介绍另一种基于(Lambda)表达式的注册方式。如果原生定义的这两种注册方式不能满足要求,利用框架提供的扩展,我们可以完成任何你想要的拦截器注册手段。阅读更多…
[4]基于Lambda表达式的拦截器注册
基于特性标注的拦截器注册方式仅限于将拦截器应用到自己定义的类型上,对于第三方提供的类型就无能为力了。对于Dora.Interception来说,拦截器注册本质上建立拦截器与一个或者多个目标方法之间的映射,所以最笨的方式就是利用反射的方式得到表示目标方法的MethodInfo对象,并将它与对应的拦截器关联在一起。这种方式虽然能够解决问题,但是编程体验很差。本篇介绍的基于表达式的拦截器注册方式利用针对目标方法或者属性的调用表达式,以一种强类型的方式获取到目标方法,极大地改善了编程体验。阅读更多…
[5]实现任意的拦截器注册方式
Dora.Interception提供了两种拦截器注册方式,一种是利用标注在目标类型、属性和方法上的InterceptorAttribute特性,另一种采用基于目标方法或者属性的调用表达式。通过提供的扩展点,我们可以任何我们希望的拦截器注册方式。阅读更多…
[6]框架设计和实现原理
从设计模式来看,Dora.Interception采用了“职责链”模式。我们将应用到同一个方法的多个拦截器以及针对目标方法的调用构建成如下所示的“调用链”。调用链在执行过程中共享同一个“调用上下文”,后者提供当前调用的上下文信息,比如目标对象、调用方法、输出参数和返回值等。每个拦截器不仅可以利用这些上下文信息执行对应的操作,还可以直接利用此上下文修改参数和返回值,并且自行决定是否继续执行后续调用。阅读更多…