zoukankan      html  css  js  c++  java
  • Dora.Interception,为.NET Core度身打造的AOP框架 [2]:以约定的方式定义拦截器

    上一篇《更加简练的编程体验》提供了最新版本的Dora.Interception代码的AOP编程体验,接下来我们会这AOP框架的编程模式进行详细介绍,本篇文章着重关注的是拦截器的定义。采用“于约定”的Interceptor定义方式是Dora.Interception区别于其他AOP框架的一个显著特征,要了解拦截器的编程约定,就得先来了解一下Dora.Interception中针对方法调用的拦截是如何实现的。

    一、针对实例的拦截

    总地来说,Dora.Interception针对方法调用的拦截机制分为两种类型,我将它称为“针对实例的拦截”和“针对类型”的拦截。针对实例的拦截应用于针对接口的方法调用,其原理如下所示:类型Foobar实现了接口IFoobar,如果需要拦截以接口的方式调用Foobar对象的某个方法,我们可以动态生成另一个用来封装Foobar对象的FoobarProxy类型,FoobarProxy同样实现IFoobar接口,我们在实现的方法中实现对Interceptor链的调用。我们最终将原始提供的Foobar对象封装成FoobarProxy对象,那么针对Foobar的方法调用将转换成针对FoobarProxy对象的调用,拦截得以实现。

    image

    二、针对类型的拦截

    如果Foobar并未实现任何接口,或者针对它的调用并非以接口的方式进行,那么我们只能采用“针对类型的拦截”,其原理如下:我们动态创建Foobar的派生类型FoobarProxy,并重写其需要被拦截的虚方法来实现对Interceptor链的调用。我们最终创建FoobarProxy对象来替换掉原始的Foobar对象,那么针对Foobar的方法调用将转换成针对FoobarProxy对象的调用,拦截得以实现。

    image

    由于这种拦截方式会直接创建代理对象,无法实现针对目标对象的封装,当我们进行DI服务注册的时候,只能指定注册服务的实现类型,不能指定一个现有的Singleton实例或者提供一个创建实例的Factory。

    三、从两个Delegate说起

    要理解Dora.Interception的设计,先得从如下这两个特殊的Delegate类型(InterceptDelegateInterceptorDelegate)说起。InterceptDelegate代表针对方法的拦截操作,作为输入参数的InvocationContext提供了当前方法调用的所有上下文信息,返回类型被设置为Task意味着Dora.Interception提供了针对基于Task的异步编程的支持。

    public delegate Task InterceptDelegate(InvocationContext context);
    public delegate InterceptDelegate InterceptorDelegate(InterceptDelegate next);
    public abstract class InvocationContext
    {
        public abstract object[] Arguments { get; }
        public abstract MethodBase Method { get; }
        public InterceptDelegate Next { get;  }
        public abstract object Proxy { get; }
        public abstract object ReturnValue { get; set; }
        public abstract object Target { get; }
        public MethodBase TargetMethod { get; }
        public abstract IDictionary<string, object> ExtendedProperties { get; }
    
        public Task ProceedAsync();
    }

    InterceptDelegate表示的是“拦截操作”,即表示作用于InvocationContext上下文上的一个Task,但它并不能表示一个拦截器对象。原因很简单,因为注册到同一个方法上的多个拦截器对象会构成一个链条,最终决定是否调用后一个拦截器或者目标方法(对于链条尾部的Interceptor)是由当前拦截器决定的,所以如果将Interceptor也表示成委托对象,它的输入应该是一个InterceptDelegate对象,表示针对后一个拦截器或者目标方法的调用,它返回的同样也是一个InterceptDelegate对象,表示将自身纳入拦截器链之后,新的拦截器链条(包括调用目标方法)所执行的操作。

    所以一个Interceptor在Dora.Interception中应该表示成一个Func<InterceptDelegate, InterceptDelegate>对象,这与ASP.NET Core的中间件管道其实是一回事。简单起见,我们为它专门定义了一个委托类型InterceptorDelegate。

    四、将一个对象转换成Interceptor

    虽然Dora.Interception总是将Interceptor对象表示成上面介绍的InterceptorDelegate类型的委托,但是为了更好的编程体验,我们可以选择采用POCO类型的方法来定义Interceptor。为了提供更好的灵活性,能够在方法中动态注入任意依赖服务,我们并不打算为这样的Interceptor类型定义一个接口。接口是一个契约,同时也是一个限制。如果类型实现某个接口,意味着必需按照规定的声明实现其方法,针对方法的服务注入将无法实现,所以Dora.Interception采用“基于约定”的方式来定义Interceptor类型。具体的约定如下

    • Interceptor只需要定义一个普通的实例类型即可。
    • Interceptor类型必须具有一个公共构造函数,它可以包含任意的参数,并支持构造器注入
    • 拦截功能实现在约定的InvokeAsync的方法中,这是一个返回类型为Task的异步方法,它的第一个参数类型为InvocationContext
    • 除了表示当前执行上下文的参数之外, 任何可以注入的服务于对象都可以定义成InvokeAsync方法的参数。
    • 当前Interceptor针对后续的Interceptor或者目标方法的调用通过调用InvocationContext的ProceedAsync方法来实现。

    如下所示的就是一个典型的Interceptor,它提供了针对构造函数和方法的注入。

    public class FoobarInterceptor
    {
        public IFoo Foo { get; }
        public string Baz { get; }  
        public FoobarInterceptor(IFoo foo, string baz)
        {
            Foo = foo;
            Baz = baz;
        }
    
        public async Task InvokeAsync(InvocationContext context, IBar bar)
        {
            await Foo.DoSomethingAsync();
            await bar.DoSomethingAsync();
            await context.ProceedAsync();
        }
    }

    [1]:更加简练的编程体验
    [2]:基于约定的拦截器定义方式
    [3]:多样性的拦截器应用方式
    [4]:与依赖注入框架的深度整合
    [5]:对拦截机制的灵活定制

  • 相关阅读:
    cinder支持nfs快照
    浏览器输入URL到返回页面的全过程
    按需制作最小的本地yum源
    创建可执行bin安装文件
    RPCVersionCapError: Requested message version, 4.17 is incompatible. It needs to be equal in major version and less than or equal in minor version as the specified version cap 4.11.
    惠普IPMI登陆不上
    Linux进程状态——top,ps中看到进程状态D,S,Z的含义
    openstack-neutron基本的网络类型以及分析
    openstack octavia的实现与分析(二)原理,架构与基本流程
    flask上下文流程图
  • 原文地址:https://www.cnblogs.com/artech/p/dora-interception-02.html
Copyright © 2011-2022 走看看