AOP框架Dora.Interception 3.0 [4]: 基于特性的拦截器注册
按照单一职责的原则,拦截器只负责需要的拦截操作的执行,至于它采用何种方式应用到目标方法上,以及它在整个拦截器管道中的位置则属于“拦截器注册”的范畴。Dora.Interception提供了几种典型的注册方法,用户也可以根据自己的需求实现自己的注册方式。
一、IInterceptorProvider
一般来说,每一个拦截器类型都对应着一个IInterceptorProvider实现类型,后者利用其Use方法负责将前者放置到拦截器管道指定的位置。如下面的代码所示,IInterceptorProvider还具有一个布尔类型的AllowMultiple属性,它表示相同类型的多一个拦截器对象是否可以同时应用到同一个方法上。如果该属性返回False,Dora.Interception只会选择其中一个。
public interface IInterceptorProvider { void Use(IInterceptorChainBuilder builder); bool AllowMultiple { get; } }
IInterceptorProvider的Use方法具有一个IInterceptorChainBuilder类型的参数。IInterceptorChainBuilder类似于ASP.NET Core中的IApplicationBuilder,我们将InterceptorDelegate 对象表示的拦截器根据指定的位置(order属性)注册到IInterceptorChainBuilder对象上,并最终利用其Build方法构建一个通过InterceptorDelegate 表示的拦截器管道。
public interface IInterceptorChainBuilder { IServiceProvider ServiceProvider { get; } IInterceptorChainBuilder Use(InterceptorDelegate interceptor, int order); InterceptorDelegate Build(); IInterceptorChainBuilder New(); }
由于大部分情况下的拦截器都是根据约定定义的,所以我们为IInterceptorChainBuilder 定义了如下的扩展方法。
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); public static IInterceptorChainBuilder Use(this IInterceptorChainBuilder builder, object interceptor, int order); }
二、基于特性的拦截器注册方式
通过在类型和方法/属性成员上标注对应特性是最常用的拦截器注册方式,这样的特性一般继承自InterceptorAttribute。如下面的代码片段所示,InterceptorAttribute实现了IInterceptorProvider接口,它的定义了Order属性来表示注册拦截器最终在管道中的位置。AllowMultiple 属性来源于应用到当前特性类型上的AttributeUsageAttribute特性的同名属性。
[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); }
对于在《拦截器的设计》中定义了那个FoobarInterceptor,我们可以将对应的特性定义成如下的形式。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] public class FoobarAttribute: InterceptorAttribute { private readonly string _baz; public FoobarAttribute (string baz) => _baz = baz; public override void Use(IInterceptorChainBuilder builder)=> builder.Use<FoobarInterceptor>(Order, _baz); }
三、拦截器和特性合二为一
如果嫌将拦截器和对应的特性分开定义太繁琐,我们可以将它们合二为一。对于在《拦截器的设计》中定义了那个FoobarInterceptor可以定义成如下的形式。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] public class FoobarAttribute:InterceptorAttribute { private readonly string _baz; public FoobarAttribute(string baz)=>_baz = baz; public async InvokeAsync(InvocationContext context, IFoo foo, IBar bar) { await PreInvokeAsync(); await context.ProceedAsync(); await PostInvokeAsync(); } public override void Use(IInterceptorChainBuilder builder) => builder.Use(this, Order, _baz); }
四、NonInterceptableAttribute
如果某个类型或者方法不应该被拦截,我们可以在上面标注一个NonIntercetableAttribute。以如下这个Foobarbaz类型为例,由于FoobarInterceptorAttribute标注在类型,意味着对应的拦截器会应用到所有可被拦截的虚方法上。如果Baz方法不应该被拦截,应该在上面标注NonIntercetableAttribute特性。
[FoobarInterceptor] public class Foobarbaz { public virtual Foo(); public virtual Bar(); [NonInterceptable] public virtual Baz(); }
除了利用NonIntercetableAttribute屏蔽所有的拦截器之外,我们还可以利用它屏蔽指定类型的拦截器。如下面的代码片段所示,Foobarbaz类型上标注了三个InterceptorAttribute,但是方法Baz只需要BazInterceptorAttribute,我们可以利用NonIntercetableAttribute特性将其他两个屏蔽掉。
[FooInterceptor] [BarInterceptor] [BazInterceptor] public class Foobarbaz { public virtual Foo(); public virtual Bar(); [NonInterceptable(typeof(FooInterceptorAttribute), typeof(BarInterceptorAttribute), )] public virtual Baz(); }
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]: 自定义拦截器注册方式