按照單一職責的原則,攔截器只負責需要的攔截操作的執行,至於它采用何種方式應用到目標方法上,以及它在整個攔截器管道中的位置則屬於“攔截器注冊”的范疇。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]: 自定義攔截器注冊方式