zoukankan      html  css  js  c++  java
  • AOP框架Dora.Interception 3.0 [3]: 拦截器设计

    对于所有的AOP框架来说,多个拦截器最终会应用到某个方法上。这些拦截器按照指定的顺序构成一个管道,管道的另一端就是针对目标方法的调用。从设计角度来将,拦截器和中间件本质是一样的,那么我们可以按照类似的模式来设计拦截器。

    一、InvocationContext

    我们为整个拦截器管道定义了一个统一的执行上下文,并将其命名为InvocationContext。如下面的代码片段所示,我们可以利用InvocationContext对象得到方法调用上下文的相关信息,其中包括两个方法(定义在接口和实现类型),目标对象、参数列表(含输入和输出参数)、返回值(可读写)。Properties 属性提供了一个自定义的属性容器,我们可以利用它来存放任意与当前方法调用上下文相关的信息。如果需要调用后续的拦截器或者目标方法(如果当前为最后一个拦截器),我们只需要直接调用ProceedAsync方法即可。

    public abstract class InvocationContext
    {    
        public abstract MethodInfo Method { get; }
        public MethodInfo TargetMethod { get; }
        public abstract object Target { get; }
        public abstract object[] Arguments { get; }
        public abstract object ReturnValue { get; set; }  
        public abstract IDictionary<string, object> Properties { get; }
        public Task ProceedAsync();
    }

    二、两个委托对象

    既然所有的拦截器都是在同一个InvocationContext上下文中执行的,那么我们可以将任意的拦截操作定义成一个Func<InvocationContext, Task>对象。Func<InvocationContext, Task>对象不仅可以表示某项单一的拦截操作,实际上包括目标方法调用在内的整个拦截器管道都可以表示成一个Func<InvocationContext, Task>对象。由于这个委托的重要性,我们将它定义成如下这个InterceptDelegate类型。

    public delegate Task InterceptDelegate(InvocationContext context);

    如果以ASP.NET Core框架的请求处理管道作为类比,那么InvocationContext相当于HttpContext,而InterceptDelegate自然对应的就是RequestDelegate。我们知道ASP.NET Core框架将中间件表示成Func<RequestDelegate, RequestDelegate>对象,那么拦截器自然就可以表示成一个Func<InterceptDelegate, InterceptDelegate>。如果读者朋友对此不太理解,可以参阅我的文章《200行代码,7个对象——让你了解ASP.NET Core框架的本质》。由于拦截器的重要性,我们也将它定义成如下这个单独的InterceptorDelegate类型。

    public delegate InterceptDelegate InterceptorDelegate(InterceptDelegate next);

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

    Dora.Interception和ASP.NET Core采用几乎一致的设计。对于ASP.NET Core来说,虽然中间件最终是通过Func<InterceptDelegate, InterceptDelegate>表示的,但是我们可以将中间件定义成一个按照约定定义的类型。Dora.Interception同样支持基于约定的拦截器类型定义。

    public class FoobarInterceptor
    {
        private readonly IFoo _foo;
        private readonly IBar _bar;
        private readonly string _baz;
    
        public FoobarInterceptor(IFoo foo, IBar bar, string baz)
        {
            _foo = foo;
            _bar = bar;
            _baz = baz;
        }
    
        public async InvokeAsync(InvocationContext context)
        {
            await PreInvokeAsync();
            await context.ProceedAsync();
            await PostInvokeAsync();
        }
    }

    如上定义的FoobarInterceptor展现了一个典型的基于约定定义的拦截器类型,它体现了如下的约定:

    • 拦截器类型是一个实例类型(不能定义成静态类型);
    • 必须具有一个公共构造函数,其中可以定义任意参数。
    • 拦截操作定义在一个名为InvokeAsync的方法中,该方法的返回类型为Task,其中包含一个InvocationContext类型的参数。如果需要调用后续拦截器管道,需要显式调用InvocationContext上下文的ProceedAsync方法。

    四、两种注入方式

    由于拦截器最终是利用.NET Core的依赖注入框架提供的,所以依赖服务可以直接注入拦截器的构造函数中。但是就服务的生命周期来讲,拦截器本质上是一个Singleton服务,我们不应该将Scoped服务注入到它的构造函数中。如果具有针对Scoped服务注入的需要,我们应该将它注入到InvokeAsync方法中。

    public class FoobarInterceptor
    {
        private readonly string _baz;
    
        public FoobarInterceptor(string baz)
        {
            _baz = baz;
        }
    
        public async InvokeAsync(InvocationContext context, IFoo foo, IBar bar)
        {
            await PreInvokeAsync();
            await context.ProceedAsync();
            await PostInvokeAsync();
        }
    }

    当Dora.Interception在调用InvokeAsync方法的时候,它会利用当前Scope的IServiceProvider对象来提供其参数。对于ASP.NET Core应用来说,如果拦截器的执行在整个请求处理的调用链中,这个IServiceProvider对象就是当前HttpContext的RequestServices属性。如果当前IServiceProvider不存在,作为根的IServiceProvider对象会被使用。

    AOP框架Dora.Interception 3.0 [1]: 编程体验
    AOP框架Dora.Interception 3.0 [2]: 实现原理
    AOP框架Dora.Interception 3.0 [3]: 拦截器设计
    AOP框架Dora.Interception 3.0 [4]: 基于特性的拦截器注册
    AOP框架Dora.Interception 3.0 [5]: 基于策略的拦截器注册
    AOP框架Dora.Interception 3.0 [6]: 自定义拦截器注册方式

  • 相关阅读:
    WUST Online Judge
    WUST Online Judge
    WUST Online Judge
    WUST Online Judge
    写在前面
    一丶Python简介
    七丶Python字典
    六丶Python列表操作
    五丶Python列表丶元组丶字典
    四丶Python运算符
  • 原文地址:https://www.cnblogs.com/artech/p/dora-interception-3-03.html
Copyright © 2011-2022 走看看