全新升级的AOP框架Dora.Interception[6]框架设计和实现原理是怎样的?

2026-04-18 00:071阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1726个文字,预计阅读时间需要7分钟。

全新升级的AOP框架Dora.Interception[6]框架设计和实现原理是怎样的?

Dora.Interception 是一个基于 GitHub 的开源编程模式,主要介绍了一种拦截模式(Interception Pattern)。以下是对其内容的简要

Dora.Interception 是一个开源项目,位于 GitHub 上(链接:[Dora.Interception](https://github.com/your-github-link))。该项目被认为是一个不错的实践,因此获得了一颗星。它主要阐述了拦截模式的编程模型及其扩展定制。下面,我们来探讨一下 Dora.Interception 的设计和实现原理。

本系列前面的五篇文章主要介绍Dora.Interception(github地址,觉得不错不妨给一颗星)的编程模式以及对它的扩展定制,现在我们来聊聊它的设计和实现原理。(拙著《ASP.NET Core 6框架揭秘》6折优惠,首印送签名专属书签)。

本系列前面的五篇文章主要介绍Dora.Interception(github地址,觉得不错不妨给一颗星)的编程模式以及对它的扩展定制,现在我们来聊聊它的设计和实现原理。(拙著《ASP.NET Core 6框架揭秘》6折优惠,首印送签名专属书签)。

目录
一、调用链抽象
二、基于约定的拦截器定义
三、基于调用上下文的依赖注入容器
四、拦截器的提供
五、调用链的构建
六、方法拦截的实现原理
七、依赖注入框架的整合
八、看看生成的代理类

一、调用链抽象

从设计模式来看,Dora.Interception采用了“职责链”模式。我们将应用到同一个方法的多个拦截器以及针对目标方法的调用构建成如下所示的“调用链”。调用链在执行过程中共享同一个“调用上下文”,后者提供当前调用的上下文信息,比如目标对象、调用方法、输出参数和返回值等。每个拦截器不仅可以利用这些上下文信息执行对应的操作,还可以直接利用此上下文修改参数和返回值,并且自行决定是否继续执行后续调用。

我们定义了如下这个抽象类InvocationContext来表示上述的调用上下文。对于参数/返回值的提取,我们设计成抽象方法以避免因装箱/拆箱带来的性能问题。拦截器针对其他服务的依赖是一个基本的需求,所以我们为InvocationContext定义了一个InvocationServices属性来提供针对当前调用的IServiceProvider对象。在默认情况下,我们会为每次调用创建一个服务范围,并利用此范围的IServiceProvider对象作为这个InvocationServices属性的值。但是对于ASP.NET Core应用,我们会直接使用针对当前请求的IServiceProvider对象。

public abstract class InvocationContext { public object Target { get; } = default!; public abstract MethodInfo MethodInfo { get; } public abstract IServiceProvider InvocationServices { get; } public IDictionary<object, object> Properties { get; } public abstract TArgument GetArgument<TArgument>(string name); public abstract TArgument GetArgument<TArgument>(int index); public abstract InvocationContext SetArgument<TArgument>(string name, TArgument value); public abstract InvocationContext SetArgument<TArgument>(int index, TArgument value); public abstract TReturnValue GetReturnValue<TReturnValue>(); public abstract InvocationContext SetReturnValue<TReturnValue>(TReturnValue value); protected InvocationContext(object target); internal InvokeDelegate Next { get; set; } = default!; public ValueTask ProceedAsync() => Next.Invoke(this); }

既然有了这样一个能够体现当前方法调用上下文的InvocationContext类型,那么上述的“调用量”就可以表示成如下这个InvokeDelegate委托。熟悉ASP.NET Core的读者可以看出Dora.Interception的调用链设计与ASP.NET Core框架的“中间件管道”几乎一致,InvocationContext和InvokeDelegate分别对应后者的HttpContext和RequestDelegate。

全新升级的AOP框架Dora.Interception[6]框架设计和实现原理是怎样的?

public delegate ValueTask InvokeDelegate(InvocationContext context);

既然将ASP.NET Core作为类比,Dora.Interception的拦截器自然就对应着ASP.NET Core的中间件了。我们知道后者体现为一个Func<RequestDelegate, RequestDelegate>委托,作为输入的RequestDelegate代表由后续中间件构建的请求处理管道,每个中间件需要利用此对象将请求分发给后续管道进行处理。Dora.Interception采用了更为简单的设计,我们将拦截器也表示成上述的InvokeDelegate委托,因为针对后续拦截器以及目标方法的调用可以利用代表调用上下文的InvocationContext对象的ProceedAsync方法来完成。如上面的代码片段所示,InvocationContext具有一个名为Next的内部属性用来表示调用调用链的下一个InvokeDelegate对象,每个拦截器在执行之前,此属性都会预先被设置,ProceedAsync方法调用的正式此属性返回的InvokeDelegate对象。

二、基于约定的拦截器定义

虽然拦截器最终由一个InvokeDelegate委托来表示,但是将其定义成一个普通的类型具有更好的编程体验。考虑到动态注入依赖服务的需要,我们并没有为拦截器定义任何的接口和基类,而是采用基于约定的定义方式。这一点与ASP.NET Core基于约定的中间件定义方法类似,由于我们的拦截器委托比中间件委托要简洁,基于约定的拦截器自然比定义中间件要简单。中间件定义按照如下的约定即可:

  • 将中间件定义成一个可以被依赖注入容器实例化的类型,一般定义成公共实例类型即可;
  • 构造函数的选择由依赖注入容器决定,构造函数可以包含任意参数;
  • 拦截操作定义在一个方法类型为ValueTask并被命名为InvokeAsync的异步方法中,该方法必须包含一个表示当前调用上下文的InvocationContext类型的参数,该参数在参数列表的位置可以任意指定。
  • InvokeAsync方法可以注入任意能够从依赖注入容器提供的对象。

按照约定定义的中间件类型或者此类型的对象最终都需要转换成一个InvokeDelegate对象,此项功能体现在IConventionalInterceptorFactory接口的两个CreateInterceptor重载方法上。第一个重载的arguments将被作为调用构造函数的参数,对于依赖注入容器无法提供的参数必须在此指定。内部类型ConventionalInterceptorFactory以表达式树的形式实现了这个接口,具体实现就不在这里展示了,有兴趣的朋友可以查看源代码。

public interface IConventionalInterceptorFactory { InvokeDelegate CreateInterceptor(Type interceptorType, params object[] arguments); InvokeDelegate CreateInterceptor(object interceptor); } internal sealed class ConventionalInterceptorFactory : IConventionalInterceptorFactory { public InvokeDelegate CreateInterceptor(Type interceptorType, params object[] arguments); public InvokeDelegate CreateInterceptor(object interceptor); }三、基于调用上下文的依赖注入容器

InvocationContext的InvocationServices属性返回针对当前调用上下文的依赖注入容器。在默认的情况下,我们会在创建InvocationContext上下文的时候创建一个服务范围,并使用此范围的IServiceProvider对象作为其InvocationServices属性。注入到InvokeAsync方法中的依赖服务是在调用时利用此IServiceProvider对象动态提供的,我们也可以在实现的InvokeAsync方法中安全的使用此对象来提供所需的服务实例。由于服务范围会在调用结束之后被自动终结,所以非单例服务实例能够被正常回收。

如下所示的IInvocationServiceScopeFactory接口表示用来创建上述服务范围的工厂,代表服务范围的IServiceScope对象由其CreateInvocationScope方法创建,InvocationServiceScopeFactory是对该接口的默认实现。

public interface IInvocationServiceScopeFactory { IServiceScope CreateInvocationScope(); } internal class InvocationServiceScopeFactory : IInvocationServiceScopeFactory { private readonly IApplicationServicesAccessor _applicationServicesAccessor; public InvocationServiceScopeFactory(IApplicationServicesAccessor applicationServicesAccessor) => _applicationServicesAccessor = applicationServicesAccessor ?? throw new ArgumentNullException(nameof(applicationServicesAccessor)); public IServiceScope CreateInvocationScope()=> _applicationServicesAccessor.ApplicationServices.CreateScope(); }

如果在一个ASP.NET Core应用中,我们因为针对当前请求的IServiceProvider(RequestServices)对象作为调用上下文的InvocationServices也许更为适合,所以在ASP.NET Core应用中注册的IInvocationServiceScopeFactory实现类型为如下这个RequestServiceScopeFactory 类型。

internal class RequestServiceScopeFactory : IInvocationServiceScopeFactory { private readonly InvocationServiceScopeFactory _factory; private readonly IHttpContextAccessor _www.558idc.com/hkgpu.html 网络转载请说明出处】

本文共计1726个文字,预计阅读时间需要7分钟。

全新升级的AOP框架Dora.Interception[6]框架设计和实现原理是怎样的?

Dora.Interception 是一个基于 GitHub 的开源编程模式,主要介绍了一种拦截模式(Interception Pattern)。以下是对其内容的简要

Dora.Interception 是一个开源项目,位于 GitHub 上(链接:[Dora.Interception](https://github.com/your-github-link))。该项目被认为是一个不错的实践,因此获得了一颗星。它主要阐述了拦截模式的编程模型及其扩展定制。下面,我们来探讨一下 Dora.Interception 的设计和实现原理。

本系列前面的五篇文章主要介绍Dora.Interception(github地址,觉得不错不妨给一颗星)的编程模式以及对它的扩展定制,现在我们来聊聊它的设计和实现原理。(拙著《ASP.NET Core 6框架揭秘》6折优惠,首印送签名专属书签)。

本系列前面的五篇文章主要介绍Dora.Interception(github地址,觉得不错不妨给一颗星)的编程模式以及对它的扩展定制,现在我们来聊聊它的设计和实现原理。(拙著《ASP.NET Core 6框架揭秘》6折优惠,首印送签名专属书签)。

目录
一、调用链抽象
二、基于约定的拦截器定义
三、基于调用上下文的依赖注入容器
四、拦截器的提供
五、调用链的构建
六、方法拦截的实现原理
七、依赖注入框架的整合
八、看看生成的代理类

一、调用链抽象

从设计模式来看,Dora.Interception采用了“职责链”模式。我们将应用到同一个方法的多个拦截器以及针对目标方法的调用构建成如下所示的“调用链”。调用链在执行过程中共享同一个“调用上下文”,后者提供当前调用的上下文信息,比如目标对象、调用方法、输出参数和返回值等。每个拦截器不仅可以利用这些上下文信息执行对应的操作,还可以直接利用此上下文修改参数和返回值,并且自行决定是否继续执行后续调用。

我们定义了如下这个抽象类InvocationContext来表示上述的调用上下文。对于参数/返回值的提取,我们设计成抽象方法以避免因装箱/拆箱带来的性能问题。拦截器针对其他服务的依赖是一个基本的需求,所以我们为InvocationContext定义了一个InvocationServices属性来提供针对当前调用的IServiceProvider对象。在默认情况下,我们会为每次调用创建一个服务范围,并利用此范围的IServiceProvider对象作为这个InvocationServices属性的值。但是对于ASP.NET Core应用,我们会直接使用针对当前请求的IServiceProvider对象。

public abstract class InvocationContext { public object Target { get; } = default!; public abstract MethodInfo MethodInfo { get; } public abstract IServiceProvider InvocationServices { get; } public IDictionary<object, object> Properties { get; } public abstract TArgument GetArgument<TArgument>(string name); public abstract TArgument GetArgument<TArgument>(int index); public abstract InvocationContext SetArgument<TArgument>(string name, TArgument value); public abstract InvocationContext SetArgument<TArgument>(int index, TArgument value); public abstract TReturnValue GetReturnValue<TReturnValue>(); public abstract InvocationContext SetReturnValue<TReturnValue>(TReturnValue value); protected InvocationContext(object target); internal InvokeDelegate Next { get; set; } = default!; public ValueTask ProceedAsync() => Next.Invoke(this); }

既然有了这样一个能够体现当前方法调用上下文的InvocationContext类型,那么上述的“调用量”就可以表示成如下这个InvokeDelegate委托。熟悉ASP.NET Core的读者可以看出Dora.Interception的调用链设计与ASP.NET Core框架的“中间件管道”几乎一致,InvocationContext和InvokeDelegate分别对应后者的HttpContext和RequestDelegate。

全新升级的AOP框架Dora.Interception[6]框架设计和实现原理是怎样的?

public delegate ValueTask InvokeDelegate(InvocationContext context);

既然将ASP.NET Core作为类比,Dora.Interception的拦截器自然就对应着ASP.NET Core的中间件了。我们知道后者体现为一个Func<RequestDelegate, RequestDelegate>委托,作为输入的RequestDelegate代表由后续中间件构建的请求处理管道,每个中间件需要利用此对象将请求分发给后续管道进行处理。Dora.Interception采用了更为简单的设计,我们将拦截器也表示成上述的InvokeDelegate委托,因为针对后续拦截器以及目标方法的调用可以利用代表调用上下文的InvocationContext对象的ProceedAsync方法来完成。如上面的代码片段所示,InvocationContext具有一个名为Next的内部属性用来表示调用调用链的下一个InvokeDelegate对象,每个拦截器在执行之前,此属性都会预先被设置,ProceedAsync方法调用的正式此属性返回的InvokeDelegate对象。

二、基于约定的拦截器定义

虽然拦截器最终由一个InvokeDelegate委托来表示,但是将其定义成一个普通的类型具有更好的编程体验。考虑到动态注入依赖服务的需要,我们并没有为拦截器定义任何的接口和基类,而是采用基于约定的定义方式。这一点与ASP.NET Core基于约定的中间件定义方法类似,由于我们的拦截器委托比中间件委托要简洁,基于约定的拦截器自然比定义中间件要简单。中间件定义按照如下的约定即可:

  • 将中间件定义成一个可以被依赖注入容器实例化的类型,一般定义成公共实例类型即可;
  • 构造函数的选择由依赖注入容器决定,构造函数可以包含任意参数;
  • 拦截操作定义在一个方法类型为ValueTask并被命名为InvokeAsync的异步方法中,该方法必须包含一个表示当前调用上下文的InvocationContext类型的参数,该参数在参数列表的位置可以任意指定。
  • InvokeAsync方法可以注入任意能够从依赖注入容器提供的对象。

按照约定定义的中间件类型或者此类型的对象最终都需要转换成一个InvokeDelegate对象,此项功能体现在IConventionalInterceptorFactory接口的两个CreateInterceptor重载方法上。第一个重载的arguments将被作为调用构造函数的参数,对于依赖注入容器无法提供的参数必须在此指定。内部类型ConventionalInterceptorFactory以表达式树的形式实现了这个接口,具体实现就不在这里展示了,有兴趣的朋友可以查看源代码。

public interface IConventionalInterceptorFactory { InvokeDelegate CreateInterceptor(Type interceptorType, params object[] arguments); InvokeDelegate CreateInterceptor(object interceptor); } internal sealed class ConventionalInterceptorFactory : IConventionalInterceptorFactory { public InvokeDelegate CreateInterceptor(Type interceptorType, params object[] arguments); public InvokeDelegate CreateInterceptor(object interceptor); }三、基于调用上下文的依赖注入容器

InvocationContext的InvocationServices属性返回针对当前调用上下文的依赖注入容器。在默认的情况下,我们会在创建InvocationContext上下文的时候创建一个服务范围,并使用此范围的IServiceProvider对象作为其InvocationServices属性。注入到InvokeAsync方法中的依赖服务是在调用时利用此IServiceProvider对象动态提供的,我们也可以在实现的InvokeAsync方法中安全的使用此对象来提供所需的服务实例。由于服务范围会在调用结束之后被自动终结,所以非单例服务实例能够被正常回收。

如下所示的IInvocationServiceScopeFactory接口表示用来创建上述服务范围的工厂,代表服务范围的IServiceScope对象由其CreateInvocationScope方法创建,InvocationServiceScopeFactory是对该接口的默认实现。

public interface IInvocationServiceScopeFactory { IServiceScope CreateInvocationScope(); } internal class InvocationServiceScopeFactory : IInvocationServiceScopeFactory { private readonly IApplicationServicesAccessor _applicationServicesAccessor; public InvocationServiceScopeFactory(IApplicationServicesAccessor applicationServicesAccessor) => _applicationServicesAccessor = applicationServicesAccessor ?? throw new ArgumentNullException(nameof(applicationServicesAccessor)); public IServiceScope CreateInvocationScope()=> _applicationServicesAccessor.ApplicationServices.CreateScope(); }

如果在一个ASP.NET Core应用中,我们因为针对当前请求的IServiceProvider(RequestServices)对象作为调用上下文的InvocationServices也许更为适合,所以在ASP.NET Core应用中注册的IInvocationServiceScopeFactory实现类型为如下这个RequestServiceScopeFactory 类型。

internal class RequestServiceScopeFactory : IInvocationServiceScopeFactory { private readonly InvocationServiceScopeFactory _factory; private readonly IHttpContextAccessor _www.558idc.com/hkgpu.html 网络转载请说明出处】