在《不一样的Interceptor》中我们着重介绍了Dora.Interception中最为核心的对象Interceptor,以及定义Interceptor类型的一些约定。由于Interceptor总是通过拦截某个方法的调用进而实现对前置或者后置操作的注入,所以我们定义的Interceptor类型总是需要与对应的目标方法进行映射。在默认的情况下,这种映射是通过在目标类型或者方法上标注特性的方式来实现的。对于任何一个Interceptor类型,我们总是需要为它定义一个对应的特性类型,这些特性具有一个共同的基类InterceptorAttribute。

目录
一、InterceptorAttribute
二、如何定义和使用InterceptorAttribute
三、InterceptorAttribute的作用域
四、屏蔽某种类型的InterceptorAttribute
五、对其他注册方式的支持

一、InterceptorAttribute

如下所示的是InterceptorAttribute特性的定义,我们可以看到它实现了一个名为IInterceptorProvider的接口,顾名思义,该接口表示为Dora.Interception的Interceptor Chain的构建系统提供单个Interceptor。昨天有人问我为什么不将Interceptor直接定义成Attribute,那么就可以直接标准在目标类型或其成员上了?对于这个问题我是这么想的:作为一个拦截器,Interceptor只需要考虑如何实现其拦截操作就可以了,而对应的Attribute的职责是如何向Interceptor Chain构建系统提供对应的Interceptor,按照我们熟悉的“单一职责”的基本设计原则,两者是应该分离的。从另一个角度来讲,InterceptorAttribute仅仅体现了Interceptor的一种“注册方式“,除了这种特性标准的注册方式,Interceptor完全还可以采用其他的注册方式,比如基于自定义映射规则,或者配置文件的方式。虽然在设计层面我将两者严格地区分开来,但是最终用户在定义Interceptor类型的时候是完全可以将两者合二为一的,我们只需要将Interceptor同时定义成继承InterceptorAttribute的特性类型就可以了。

IInterceptorProvider接口具有两个成员,其中核心成员Use实现了针对Interceptor的注册。至于另一个名为AllowMutiple的属性,它表示由通过具有相同类型的InterceptorProvider提供的Interceptor是否可以同时应用到同一个方法上。

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method| AttributeTargets.Property, AllowMultiple = false)]
public abstract class InterceptorAttribute : Attribute, IInterceptorProvider
{
public int Order { get; set; }
public bool AllowMultiple {get;}
public abstract void Use(IInterceptorChainBuilder builder);
} public interface IInterceptorProvider
{
void Use(IInterceptorChainBuilder builder);
bool AllowMultiple { get; }
}

IInterceptorProvider接口的Use方法具有一个类型为IInterceptorChainBuilder的参数,该接口表示一个用于构建Interceptor Chain的InterceptorChainBuilder对象,我们可以调用其Use方法向它提供一个Interceptor对象。该方法的第一个参数表示提供的Interceptor对象,而第二个参数(order)则表示这个Interceptor在最终构建的Interceptor Chain中所处的位置。除了这个Use方法,我们还额外定义了两个同名的扩展方法,其中Use<T>是我们最为常用的。

 public interface IInterceptorChainBuilder
{
IInterceptorChainBuilder Use(InterceptorDelegate interceptor, int order);
... } public static class InterceptorChainBuilderExtensions
{
public static IInterceptorChainBuilder Use<TInterceptor>(this IInterceptorChainBuilder builder, int order, params object[] arguments);
public static IInterceptorChainBuilder Use(this IInterceptorChainBuilder builder, Type interceptorType, int order, params object[] arguments);
}

我们现在在回头看看InterceptorAttribute类型,这个类型具有一个Order属性正好对应于上面Use方法的order参数,而实现的AllowMultiple方法在默认的情况下与AttributeUsage.AllowMultiple属性具有相同的值。所有派生于InterceptorAttribute的子类都需要重写用于提供对应Interceptor的Use方法,一般来说我们只需要调用作为参数的IInterceptorChainBuilder对应的Use方法即可。在标注具有某个InterceptorAttribute的时候,我们可以按照如下的方式通过Order属性控制Interceptor的执行顺序:

 public class Foobar
{
[Foo(Order = 1)]
[Bar(Order = 2)]
public virtual void Invoke()
{}
}

二、如何定义InterceptorAttribute

当我们为某个Interceptor类型定义对应InterceptorProvider特性的时候,只需要继承InterceptorAttribute类型,并实现其Use方法即既可以,但是在调用IInterceptorChainBuilder的Use方法的时候针对参数的指定则取决于Interceptor类型构造函数的定义,以及针对DI的服务注册。举个简单的例子,如下这个FoobarInterceptor的构造函数具有四个参数,除了第一个必需的参数由Interceptor的激活系统自行提供之外,其它的三个参数要么通过DI系统的ServiceProvider来提供,要么有对应的InterceptorProvdier来提供。

 public class FoobarInterceptor
{
public FoobarInterceptor(InterceptDelegate next, IFoo foo, IBar bar, string baz);
public Task InvokeAsync(InvocationContext context);
}

假设前面两个参数foo和bar由DI系统的ServiceProvider来提供,当我们为InterceptorProvider定义InterceptorAttribute的时候,实现的Use方法只需要提供baz参数的值就可以了,那么我们可以采用如下的方式来定义这个InterceptorAttribute。

 [AttributeUsage( AttributeTargets.Method| AttributeTargets.Class| AttributeTargets.Parameter, AllowMultiple = false )]
public class FoobarAttribute : InterceptorAttribute
{
public string Baz { get; } public FoobarAttribute(string baz)
{
this.Baz = baz;
}
public override void Use(IInterceptorChainBuilder builder)
{
builder.Use<FoobarInterceptor>(this.Order, this.Baz);
}
}

假设我们不希望以DI的方式来提供foo和bar两个参数,我们可以按照如下的方式在调用IInterceptorChainBuilder的Use方法是显式提供这两个参数的值。

 [AttributeUsage( AttributeTargets.Method| AttributeTargets.Class| AttributeTargets.Property, AllowMultiple = false )]
public class FoobarAttribute : InterceptorAttribute
{
public string Baz { get; } public FoobarAttribute(string baz)
{
this.Baz = baz;
}
public override void Use(IInterceptorChainBuilder builder)
{
builder.Use<FoobarInterceptor>(this.Order, this.Baz, new Bar(), new Foo());
}
}

三、InterceptorAttribute的作用域

Dora.Interception支持标注在类型、方法和属性上的InterceptorAttribute。对于应用在类型上的InterceptorAttribute特性,由它提供的Interceptor实际上是应用到该类型上的所有可以被拦截的方法上。如果InterceptorAttribute被标注到属性成员上,意味着该属性的Get和Set方法同时应用了对应的Interceptor。如果我们只需要将Interceptor应用到某个属性的Get方法或者Set方法上,我们可以选择将 对应的InterceptorAttribute单独应用到Get或者Set方法上。除此之外Dora.Interception还支持继承的InterceptorAttribute,也就是说标注到基类上的InterceptorAttribute会自动被子类继承。

在解析InterceptorAttribute特性的时候,我特意屏蔽了应用在接口上的特性,我是这样考虑的:接口是一个多方契约,它不应该考虑实现的细节,而基于AOP的拦截则属于单方的实现行为,所以InterceptorAttribute是不应该标注在接口上。我知道很多AOP框架(比如Unity)是可以直接将Interceptor(CallHandler)应用在接口上的,但是我觉得这一点不妥。

值得一提的是InterceptorAttribute的AllowMultiple属性,如果该属性返回True,意味针对这个类型的所有特性标注都是有效的。如果我们希望某个InterceptorAttribute提供的Interceptor在最终的目标方法上只能执行一次,我们需要通过应用AttributeUsage特性并将其AllowMultiple设置为False。我们知道AttributeUsage的AllowMultiple属性只能控制对应的特性在同一个目标成员上的标注次数,也就是说对于一个AllowMultiple设置为False的Attribute,我们可以同时将其标注到类型和它的成员上的。Dora.Interception对此作了相应的处理,确保只有更接近目标方法上的特性采用有效的。

以如下的定义为例,如果FoobarAttribute的AllowMultiple被设置为False,对应方法Foo,只有应用在它自身方法上的FoobarAttribute有效。对于Bar属性的Get和Set方法,只有应用在其属性上的FoobarAttribute有效。对于Baz属性,应用在自身属性上的FoobarAttribute只会应用到其Set方法上,至于其Get方法则会使用应用在自身方法上的FoobarAttribute。

 [Foobar]
public class Demo
{
[Foobar]
public virtual void Foo()
{
Console.WriteLine("Demo.Invoke() is invoked");
} [Foobar]
public virtual Bar Bar {get;set;} [Foobar]
public virtual Baz Baz { [Foobar]get; set; }
}

四、屏蔽某种类型的InterceptorAttribute

如果某种类型的InterceptorAttribute提供的Interceptor只适用于某个类型的大部分成员,我们可以选择单独将它标注到这些成员上。我们也可以采用排除,直接将该InterceptorAttribute标准到类型上,然后再不适用的类型成员上标注一个具有如下定义的NonInterceptableAttribute特性。当我们在使用NonInterceptableAttribute特性的时候,如果没有指定任何参数,意味着目标类型或者类型成员(方法或者属性)是不需要被拦截的。如果指定了IntercecptorProvider的类型,它只会屏蔽对应的IntercecptorProvider类型。

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method| AttributeTargets.Property)]
public class NonInterceptableAttribute : Attribute
{
public Type[] InterceptorProviderTypes { get; }
public NonInterceptableAttribute(params Type[] interceptorProviderTypes)
}

以如下的代码片段为例,基类Foo上标注了两个InterceptorAttribute(Interceptor1Attribute和Interceptor2Attribute),由于其子类Bar上标注了NonInterceptableAttribute,所以整个类型都是不需要被拦截的。至于另一个子类 Baz,它会继承者两个InterceptorAttribute,但是Invoke放上通过标注NonInterceptableAttribute屏蔽了Interceptor1Attribute,所以只有Interceptor2Attribute对它来说是有效的。

 [Interceptor1]
[Interceptor2]
public class Foo
{
...
} [NonInterceptable]
public class Bar : Foo
{
[Interceptor1]
public virtual void Invoke();
} public class Baz : Foo
{
[NonInterceptable(typeof(Interceptor1Attribute))]
public virtual void Invoke();
}

五、对其他注册方式的支持

我在设计Dora.Interception的时候参考了很多主流的AOP框架,而我是Unity多年深度使用者,曾经多次研究过Unity.Interception的源代码。我觉得很多的AOP框架都过于复杂,刻意地添加了一些我觉得不它适合的特性,所以我的Dora.Interception在很多方面实际上在做减法。在Interceptor的注册方面,实际上在开发的时候是提供了基于MatchingRule的注册方式(这也是参考了Unity.Interception),利用定义的各种MatchingRule,我们可以采用各种匹配模式(比如类型/方法/属性名称、命名空间、程序集名称以及标注的Tag)将某种的Interceptor应用到满足规则的类型或者方法上。但是根据我个人的使用经验来看,由于这种匹配模式过于“模糊”,我们非常容易无疑之间就扩大了某种Interceptor的应用范围。所以我们在发布Dora.Interception的时候将这种注册方式移除了,所以目前为止只支持特性标注这一种注册方式。不过Dora.Interception在这个方面给出了扩展点,如果需要可以自行实现。也许在下一个版本,我会提供一些额外的注册方式,但是一定会要求这样的注册方式是“精准的”和“明确的”。

Dora.Interception, 为.NET Core度身打造的AOP框架 [1]:全新的版本
Dora.Interception, 为.NET Core度身打造的AOP框架 [2]:不一样的Interceptor定义方式
Dora.Interception, 为.NET Core度身打造的AOP框架 [3]:Interceptor的注册
Dora.Interception, 为.NET Core度身打造的AOP框架[4]:演示几个典型应用

Dora.Interception, 一个为.NET Core度身打造的AOP框架[3]:Interceptor的注册的更多相关文章

  1. Dora.Interception, 一个为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式

    相较于社区其他主流的AOP框架,Dora.Interception在Interceptor提供了完全不同的编程方式.我们并没有为Interceptor定义一个接口,正是因为不需要实现一个预定义的接口, ...

  2. Dora.Interception,为.NET Core度身打造的AOP框架 [3]:多样化拦截器应用方式

    在<以约定的方式定义拦截器>中,我们通过对拦截器的介绍了Dora.Interception的两种拦截机制,即针对接口的“实例拦截”针对虚方法的“类型拦截”.我们介绍了拦截器的本质以及基于约 ...

  3. Dora.Interception: 一个为.NET Core度身定制的AOP框架

    多年从事框架设计开发使我有了一种强迫症,那就是见不得一个应用里频繁地出现重复的代码.之前经常Review别人的代码,一看到这样的程序,我就会想如何将这些重复的代码写在一个地方,然后采用“注入”的方式将 ...

  4. Dora.Interception,为.NET Core度身打造的AOP框架 [1]:更加简练的编程体验

    很久之前开发了一个名为Dora.Interception的开源AOP框架(github地址:https://github.com/jiangjinnan/Dora,如果你觉得这个这框架还有那么一点价值 ...

  5. Dora.Interception,为.NET Core度身打造的AOP框架 [5]:轻松地实现与其他AOP框架的整合

    这里所谓的与第三方AOP框架的整合不是说改变Dora.Interception现有的编程,而是恰好相反,即在不改变现有编程模式下采用第三方AOP框架或者自行实现的拦截机制.虽然我们默认提供基于IL E ...

  6. Dora.Interception,为.NET Core度身打造的AOP框架 [4]:与依赖注入框架的无缝集成

    Dora.Interception最初的定位就是专门针对.NET Core的AOP框架,所以在整个迭代过程中我大部分是在做减法.对于.NET Core程序开发来说,依赖注入已经成为无处不在并且“深入骨 ...

  7. Dora.Interception,为.NET Core度身打造的AOP框架 [2]:以约定的方式定义拦截器

    上一篇<更加简练的编程体验>提供了最新版本的Dora.Interception代码的AOP编程体验,接下来我们会这AOP框架的编程模式进行详细介绍,本篇文章着重关注的是拦截器的定义.采用“ ...

  8. Dora.Interception,为.NET Core度身打造的AOP框架:全新的版本

    Dora.Interception 1.0(Github地址:可以访问GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段时间了,最近花了点时间将它升级 ...

  9. Dora.Interception, 为.NET Core度身打造的AOP框架[4]:演示几个典型应用

    为了帮助大家更深刻地认识Dora.Interception,并更好地将它应用到你的项目中,我们通过如下几个简单的实例来演示几个常见的AOP应用在Dora.Interception下的实现.对于下面演示 ...

随机推荐

  1. ExperDot的博客目录导航

    最近活动 我更新了博客!粒子系统:从零开始画一棵树 Github:[ UWP ] [ JavaScript ] 自然编程  奇幻元纪 上帝创世篇:如何画一颗静态树 女娲补天篇:仿人工拼接碎片 吴刚伐桂 ...

  2. 学习时用的软件最新 开发环境为Visual Studio 2010,数据库为SQLServer2005,使用.net 4.0开发。 超市管理系统

    一.源码特点 1.采用典型的三层架构进行开发.模板分离,支持生成静态 伪静态..购物车.登陆验证.div+css.js等技术二.功能介绍 1.本源码是一个超市在线购物商城源码,该网上商城是给超市便利店 ...

  3. Linux下磁盘监控及系统版本-CPU-内存等查看

    1.磁盘IO监控工具 iotop 输入命令:iotop   主要查看程序使用的磁盘IO的信息 安装:yum -y install iotop 第一行:10:01:23 — 当前系统时间126 days ...

  4. eval基础,基础用法及解析json

    <body> <!-- eval 的使用:eval(string) 计算某个字符串,并执行其中的js代码 字符串上运用 eval() eval("x = 10;y = 2; ...

  5. Android 一排按钮居中显示

    将一排按钮放在LinearLayout中,设置LinearLayout的Android gravity属性为center_vertical(垂直居中)

  6. c# 去除字符串中重复字符

    String.Join 和 Distinct 方法 https://www.cnblogs.com/louby/p/6224960.html 1.在写程序中经常操作字符串,需要去重,以前我的用方式利用 ...

  7. sql sever[基本] ''增删改'' 随笔

    结构语言分类 DDL(数据定义语言)  create  drop  alter   创建删除以及修改数据库,表,存储过程,触发器,索引.... DML(数据操作语言)   insert  delete ...

  8. 如何复制Google浏览器的控制台内容

    今天在调用第三方的接口,对着文档,传参数,老是报参数错误,没办法只能把参数打印出来看看,在Google控制台上看,费劲,就想复制出来,格式化一下,然后对着文档进行对比. console.info(JS ...

  9. thinkinginjava学习笔记05_访问权限

    Java中访问权限等级从大到小依次为:public.protected.包访问权限(没有关键词).private: 以包访问权限为界限,public.protected分别可以被任意对象和继承的对象访 ...

  10. XML的解析(DOM以及SAX方式)

    感谢http://blog.csdn.net/redarmy_chen/article/details/12951649(关于SAX解析)以及http://blog.csdn.net/zhangerq ...