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]: 自定義攔截器註冊方式